1215 lines
36 KiB
VB.net
1215 lines
36 KiB
VB.net
Imports System.Drawing
|
|
Imports System.IO
|
|
Imports System.Text
|
|
Imports Org.BouncyCastle.Pkcs
|
|
Imports TSpdf
|
|
Imports TSpdf.Bouncycastle.Cert
|
|
Imports TSpdf.Bouncycastle.Crypto
|
|
Imports TSpdf.Commons.Bouncycastle.Cert
|
|
Imports TSpdf.Commons.Bouncycastle.Crypto
|
|
Imports TSpdf.Forms
|
|
Imports TSpdf.IO.Font
|
|
Imports TSpdf.IO.Font.Constants
|
|
Imports TSpdf.IO.Image
|
|
Imports TSpdf.Kernel.Colors
|
|
Imports TSpdf.Kernel.Font
|
|
Imports TSpdf.Kernel.Pdf
|
|
Imports TSpdf.Kernel.Pdf.Canvas
|
|
Imports TSpdf.Layout
|
|
Imports TSpdf.Layout.Element
|
|
Imports TSpdf.Layout.Properties
|
|
Imports TSpdf.Signatures
|
|
Imports TSpdfUtils.Firmas
|
|
Imports tsPDFUtilsCore
|
|
|
|
|
|
|
|
' ============================================================================
|
|
'
|
|
'
|
|
' Funciones auxiliares para sustituir el Bitmap/GDI+ de la apariencia visible
|
|
' de una firma por elementos dibujados directamente sobre el Layer2 de iText 7.
|
|
'
|
|
' REQUISITOS:
|
|
' - DatosTextos
|
|
' - DatosImagenFirma
|
|
' - DatosFuente
|
|
' - EsquinaEnum
|
|
' - AlineamientoEnum
|
|
'
|
|
' Estas clases/enums NO se declaran aquí porque ya existen en el proyecto.
|
|
'
|
|
' La conversión mantiene el sistema de coordenadas del código antiguo:
|
|
' - distancias/posiciones: unidades equivalentes a los píxeles del Bitmap
|
|
' original de 600 DPI.
|
|
' - iText: puntos PDF.
|
|
'
|
|
' 600 DPI -> 72 puntos/pulgada
|
|
' 1 unidad antigua = 72 / 600 puntos.
|
|
' ============================================================================
|
|
|
|
Public Class FirmaCanvas
|
|
|
|
Private Const DPI_BITMAP_ORIGINAL As Single = 600.0F
|
|
Private Const PUNTOS_POR_PULGADA As Single = 72.0F
|
|
Private Const FACTOR_BITMAP_A_PDF As Single = PUNTOS_POR_PULGADA / DPI_BITMAP_ORIGINAL
|
|
|
|
Public Shared Sub FirmaPDF(bPfx As Byte(), ByVal ClavePFX As String, ByVal ClavePDF As String, ByVal PdfOrigen As Byte(), ByVal pdfDestino As Stream, ByVal Razon As String, ByVal Contacto As String, ByVal Localizacion As String, ByVal listadoTextos As List(Of DatosTextos), ByVal listadoImagenes As List(Of DatosImagenFirma), Optional listadoFuentes As List(Of DatosFuente) = Nothing, Optional EliminarFirmasAnteriores As Boolean = False, Optional ObviarSeguridad As Boolean = True, Optional NombreCampoFirma As String = "", Optional DatosCampoFirma As DatosNuevoCampoFirma = Nothing, Optional BloquearDocumento As Boolean = False)
|
|
Dim StreamPfx As New MemoryStream(bPfx)
|
|
Dim fso = New MemoryStream(PdfOrigen)
|
|
FirmaPDFVisible(StreamPfx, ClavePFX, ClavePDF, fso, pdfDestino, Razon, Contacto, Localizacion, listadoTextos, listadoImagenes, listadoFuentes, EliminarFirmasAnteriores, ObviarSeguridad, NombreCampoFirma, DatosCampoFirma, BloquearDocumento)
|
|
End Sub
|
|
|
|
|
|
|
|
Public Shared Sub FirmaPDFVisible(bPfx As Byte(), ByVal ClavePFX As String, ByVal ClavePDF As String, ByVal PdfOrigen As String, ByVal pdfDestino As String, ByVal Razon As String, ByVal Contacto As String, ByVal Localizacion As String, ByVal listadoTextos As List(Of DatosTextos), ByVal listadoImagenes As List(Of DatosImagenFirma), Optional listadoFuentes As List(Of DatosFuente) = Nothing, Optional EliminarFirmasAnteriores As Boolean = False, Optional ObviarSeguridad As Boolean = True, Optional NombreCampoFirma As String = "", Optional DatosCampoFirma As DatosNuevoCampoFirma = Nothing, Optional BloquearDocumento As Boolean = False)
|
|
Dim StreamPfx As New MemoryStream(bPfx)
|
|
Dim fso = New MemoryStream(System.IO.File.ReadAllBytes(PdfOrigen))
|
|
Dim fs As New FileStream(pdfDestino, FileMode.Create)
|
|
FirmaPDFVisible(StreamPfx, ClavePFX, ClavePDF, fso, fs, Razon, Contacto, Localizacion, listadoTextos, listadoImagenes, listadoFuentes, EliminarFirmasAnteriores, ObviarSeguridad, NombreCampoFirma, DatosCampoFirma, BloquearDocumento)
|
|
fs.Close()
|
|
End Sub
|
|
|
|
Public Shared Sub FirmaPDFVisible(ByVal StreamPFX As Stream, ByVal ClavePFX As String, ByVal ClavePDF As String, ByVal PdfOrigen As Stream, ByVal pdfDestino As Stream, ByVal Razon As String, ByVal Contacto As String, ByVal Localizacion As String, ByVal listadoTextos As List(Of DatosTextos), ByVal listadoImagenes As List(Of DatosImagenFirma), Optional listadoFuentes As List(Of DatosFuente) = Nothing, Optional EliminarFirmasAnteriores As Boolean = False, Optional ObviarSeguridad As Boolean = True, Optional NombreCampoFirma As String = "", Optional DatosCampoFirma As DatosNuevoCampoFirma = Nothing, Optional BloquearDocumento As Boolean = False)
|
|
Try
|
|
Dim ms As New MemoryStream
|
|
Dim reader As New PdfReader(PdfOrigen)
|
|
reader.SetUnethicalReading(ObviarSeguridad)
|
|
Dim sp As New StampingProperties()
|
|
' Dim ListaCampos() As String
|
|
Dim medidas As Kernel.Geom.Rectangle
|
|
If NombreCampoFirma = "" Then
|
|
medidas = New Kernel.Geom.Rectangle(DatosCampoFirma.CoordenadaX, DatosCampoFirma.CoordenadaY, DatosCampoFirma.Ancho, DatosCampoFirma.Alto)
|
|
Else
|
|
Dim pdfDoc As PdfDocument = New PdfDocument(reader)
|
|
Dim pdfAcrof = PdfAcroForm.GetAcroForm(pdfDoc, False)
|
|
Dim cf = pdfAcrof.GetField(NombreCampoFirma)
|
|
If cf Is Nothing Then
|
|
medidas = New Kernel.Geom.Rectangle(DatosCampoFirma.CoordenadaX, DatosCampoFirma.CoordenadaY, DatosCampoFirma.Ancho, DatosCampoFirma.Alto)
|
|
Else
|
|
medidas = cf.GetWidgets(0).GetRectangle.ToRectangle
|
|
End If
|
|
pdfDoc.Close()
|
|
PdfOrigen.Seek(0, 0)
|
|
reader = New PdfReader(PdfOrigen)
|
|
End If
|
|
If EliminarFirmasAnteriores = False Then
|
|
sp.UseAppendMode()
|
|
If ClavePDF <> "" Then
|
|
Dim bc = System.Text.Encoding.ASCII.GetBytes(ClavePDF)
|
|
Dim props As WriterProperties = New WriterProperties().SetStandardEncryption(Nothing, bc, EncryptionConstants.ALLOW_PRINTING, EncryptionConstants.ENCRYPTION_AES_128 Or EncryptionConstants.DO_NOT_ENCRYPT_METADATA)
|
|
Dim writer As PdfWriter = New PdfWriter(ms, props)
|
|
Dim pdfDoc As PdfDocument = New PdfDocument(reader, writer)
|
|
|
|
Dim pdfAcrof = PdfAcroForm.GetAcroForm(pdfDoc, False)
|
|
' If NombreCampoFirma <> "" Then medidas = pdfAcrof.GetField(NombreCampoFirma).GetWidgets(0).GetRectangle.ToRectangle
|
|
pdfDoc.Close()
|
|
Dim rp As New ReaderProperties()
|
|
rp.SetPassword(bc)
|
|
reader = New PdfReader(New MemoryStream(ms.ToArray), rp)
|
|
|
|
End If
|
|
reader.SetUnethicalReading(ObviarSeguridad)
|
|
|
|
Else
|
|
Dim writer As PdfWriter
|
|
If ClavePDF <> "" Then
|
|
Dim bc = System.Text.Encoding.ASCII.GetBytes(ClavePDF)
|
|
Dim props = New WriterProperties().SetStandardEncryption(Nothing, bc, EncryptionConstants.ALLOW_PRINTING, EncryptionConstants.ENCRYPTION_AES_128 Or EncryptionConstants.DO_NOT_ENCRYPT_METADATA)
|
|
writer = New PdfWriter(ms, props)
|
|
Else
|
|
writer = New PdfWriter(ms)
|
|
End If
|
|
Dim pdfDoc As PdfDocument = New PdfDocument(reader, writer)
|
|
Dim pdfAcrof = PdfAcroForm.GetAcroForm(pdfDoc, False)
|
|
pdfAcrof.FlattenFields()
|
|
pdfDoc.Close()
|
|
If ClavePDF = "" Then
|
|
reader = New PdfReader(New MemoryStream(ms.ToArray))
|
|
Else
|
|
Dim pr As New ReaderProperties()
|
|
pr.SetPassword(Encoding.ASCII.GetBytes(ClavePDF))
|
|
reader = New PdfReader(New MemoryStream(ms.ToArray), pr)
|
|
End If
|
|
reader.SetUnethicalReading(ObviarSeguridad)
|
|
End If
|
|
|
|
Dim cpassword = ClavePFX.ToCharArray
|
|
Dim [alias] As String = Nothing
|
|
Dim pk12 As Pkcs12Store = New Pkcs12Store(StreamPFX, cpassword)
|
|
|
|
For Each a In pk12.Aliases
|
|
[alias] = (CType(a, String))
|
|
|
|
If pk12.IsKeyEntry([alias]) Then
|
|
Exit For
|
|
End If
|
|
Next
|
|
|
|
Dim pk As IPrivateKey = New PrivateKeyBC(pk12.GetKey([alias]).Key)
|
|
Dim pks = New PrivateKeySignature(pk, DigestAlgorithms.SHA256)
|
|
|
|
Dim ce As X509CertificateEntry() = pk12.GetCertificateChain([alias])
|
|
Dim chain = New IX509Certificate(ce.Length - 1) {}
|
|
|
|
For k As Integer = 0 To ce.Length - 1
|
|
chain(k) = New X509CertificateBC(ce(k).Certificate)
|
|
Next
|
|
|
|
Dim OCSPVerifier = New OCSPVerifier(Nothing, Nothing)
|
|
Dim ocspClient = New OcspClientBouncyCastle(OCSPVerifier)
|
|
Dim crlClients = New List(Of ICrlClient)
|
|
Dim listadoDatosCertificado = ce(0).Certificate.SubjectDN.GetValueList
|
|
Dim nombre = listadoDatosCertificado(4).ToString
|
|
|
|
Dim pdfSign As New PdfSigner(reader, pdfDestino, sp)
|
|
If NombreCampoFirma = "" Then
|
|
pdfSign.SetFieldName(DatosCampoFirma.NombreCampo)
|
|
Else
|
|
pdfSign.SetFieldName(NombreCampoFirma)
|
|
End If
|
|
|
|
Dim appearance As PdfSignatureAppearance = pdfSign.GetSignatureAppearance()
|
|
|
|
appearance.
|
|
SetReason(Razon).
|
|
SetLocation(Localizacion).
|
|
SetContact(Contacto).
|
|
SetPageRect(medidas).
|
|
SetLayer2Text("").
|
|
SetReuseAppearance(False)
|
|
|
|
Dim layer2 = appearance.GetLayer2()
|
|
|
|
Dim pdfCanvas As New PdfCanvas(
|
|
layer2,
|
|
pdfSign.GetDocument())
|
|
|
|
Dim layoutCanvas As New Canvas(
|
|
pdfCanvas,
|
|
medidas)
|
|
|
|
DibujarFirmaEnCanvas(
|
|
layoutCanvas,
|
|
listadoTextos,
|
|
listadoImagenes,
|
|
listadoFuentes,
|
|
medidas)
|
|
|
|
layoutCanvas.Close()
|
|
|
|
|
|
|
|
|
|
|
|
If BloquearDocumento Then
|
|
pdfSign.SetCertificationLevel(PdfSigner.CERTIFIED_NO_CHANGES_ALLOWED)
|
|
End If
|
|
pdfSign.SignDetached(pks, chain, crlClients, ocspClient, Nothing, 0, PdfSigner.CryptoStandard.CADES)
|
|
|
|
reader.Close()
|
|
|
|
Catch ex As Exception
|
|
Throw New Exception(ex.Message, ex)
|
|
End Try
|
|
End Sub
|
|
|
|
|
|
' =========================================================================
|
|
' FUNCIÓN PRINCIPAL
|
|
' =========================================================================
|
|
'
|
|
' Dibuja textos e imágenes directamente en el Canvas de Layer2.
|
|
'
|
|
' Ejemplo de uso:
|
|
'
|
|
' Dim layer2 = appearance.GetLayer2()
|
|
' Dim pdfCanvas As New PdfCanvas(layer2, pdfSign.GetDocument())
|
|
' Dim layoutCanvas As New Canvas(pdfCanvas, medidas)
|
|
'
|
|
' DibujarFirmaEnCanvas(layoutCanvas, listadoTextos, listadoImagenes,
|
|
' listadoFuentes, medidas)
|
|
'
|
|
' layoutCanvas.Close()
|
|
'
|
|
' =========================================================================
|
|
|
|
Public Shared Sub DibujarFirmaEnCanvas(
|
|
ByVal canvas As Canvas,
|
|
ByVal listadoTextos As List(Of DatosTextos),
|
|
ByVal listadoImagenes As List(Of DatosImagenFirma),
|
|
ByVal listadoFuentes As List(Of DatosFuente),
|
|
ByVal medidas As TSpdf.Kernel.Geom.Rectangle)
|
|
|
|
If canvas Is Nothing Then
|
|
Throw New ArgumentNullException(NameOf(canvas))
|
|
End If
|
|
|
|
If medidas Is Nothing Then
|
|
Throw New ArgumentNullException(NameOf(medidas))
|
|
End If
|
|
|
|
|
|
' Dimensiones del Bitmap original de 600 DPI.
|
|
'
|
|
' El Bitmap antiguo se creaba así:
|
|
'
|
|
' anchoPixeles = anchoPDF * 600 / 72
|
|
' altoPixeles = altoPDF * 600 / 72
|
|
'
|
|
Dim anchoBitmap As Integer =
|
|
CInt(Math.Round(
|
|
medidas.GetWidth() / FACTOR_BITMAP_A_PDF))
|
|
|
|
Dim altoBitmap As Integer =
|
|
CInt(Math.Round(
|
|
medidas.GetHeight() / FACTOR_BITMAP_A_PDF))
|
|
|
|
|
|
' =====================================================================
|
|
' TEXTOS
|
|
' =====================================================================
|
|
|
|
If listadoTextos IsNot Nothing Then
|
|
|
|
For Each datosTexto As DatosTextos In listadoTextos
|
|
|
|
If datosTexto Is Nothing Then
|
|
Continue For
|
|
End If
|
|
|
|
DibujarTextoFirma(
|
|
canvas,
|
|
datosTexto,
|
|
listadoFuentes,
|
|
anchoBitmap,
|
|
altoBitmap)
|
|
|
|
Next
|
|
|
|
End If
|
|
|
|
|
|
' =====================================================================
|
|
' IMÁGENES
|
|
' =====================================================================
|
|
|
|
If listadoImagenes IsNot Nothing Then
|
|
|
|
For Each datosImagen As DatosImagenFirma In listadoImagenes
|
|
|
|
If datosImagen Is Nothing Then
|
|
Continue For
|
|
End If
|
|
|
|
DibujarImagenFirma(
|
|
canvas,
|
|
datosImagen,
|
|
anchoBitmap,
|
|
altoBitmap)
|
|
|
|
Next
|
|
|
|
End If
|
|
|
|
End Sub
|
|
|
|
|
|
' =========================================================================
|
|
' TEXTO
|
|
' =========================================================================
|
|
|
|
Private Shared Sub DibujarTextoFirma(
|
|
ByVal canvas As Canvas,
|
|
ByVal datosTexto As DatosTextos,
|
|
ByVal listadoFuentes As List(Of DatosFuente),
|
|
ByVal anchoFirmaBitmap As Integer,
|
|
ByVal altoFirmaBitmap As Integer)
|
|
|
|
Dim font As PdfFont =
|
|
ObtenerFuentePdf(
|
|
datosTexto.NombreFuente,
|
|
listadoFuentes)
|
|
|
|
|
|
Dim tamanoFuente As Single =
|
|
datosTexto.size
|
|
|
|
If tamanoFuente <= 0 Then
|
|
tamanoFuente = 1
|
|
End If
|
|
|
|
|
|
' ---------------------------------------------------------------------
|
|
' Medición aproximada equivalente a MeasureString().
|
|
'
|
|
' GDI+ y las fuentes PDF no miden exactamente igual. Esta medición
|
|
' sirve para mantener el posicionamiento por esquina.
|
|
' ---------------------------------------------------------------------
|
|
|
|
Dim anchoTextoPdf As Single =
|
|
font.GetWidth(
|
|
If(datosTexto.texto, String.Empty),
|
|
tamanoFuente)
|
|
|
|
Dim altoTextoPdf As Single =
|
|
ObtenerAltoTexto(font, tamanoFuente)
|
|
|
|
|
|
Dim anchoTextoBitmap As Integer =
|
|
Math.Max(
|
|
1,
|
|
CInt(Math.Ceiling(
|
|
anchoTextoPdf / FACTOR_BITMAP_A_PDF)))
|
|
|
|
|
|
Dim altoTextoBitmap As Integer =
|
|
Math.Max(
|
|
1,
|
|
CInt(Math.Ceiling(
|
|
altoTextoPdf / FACTOR_BITMAP_A_PDF)))
|
|
|
|
|
|
' ---------------------------------------------------------------------
|
|
' Utilizamos exactamente la lógica de calcularX/calcularY del código
|
|
' antiguo, trabajando en las unidades del Bitmap virtual de 600 DPI.
|
|
' ---------------------------------------------------------------------
|
|
|
|
Dim coordenadaXBitmap As Integer =
|
|
CalcularX(
|
|
datosTexto.distanciaX,
|
|
datosTexto.distanciaY,
|
|
datosTexto.PosicionEsquina,
|
|
anchoFirmaBitmap,
|
|
altoFirmaBitmap,
|
|
datosTexto.anguloRotacion,
|
|
anchoTextoBitmap,
|
|
altoTextoBitmap)
|
|
|
|
|
|
Dim coordenadaYBitmap As Integer =
|
|
CalcularY(
|
|
datosTexto.distanciaY,
|
|
datosTexto.distanciaX,
|
|
datosTexto.PosicionEsquina,
|
|
anchoFirmaBitmap,
|
|
altoFirmaBitmap,
|
|
datosTexto.anguloRotacion,
|
|
anchoTextoBitmap,
|
|
altoTextoBitmap)
|
|
|
|
|
|
' ---------------------------------------------------------------------
|
|
' Bitmap -> PDF.
|
|
'
|
|
' El Bitmap antiguo tiene origen arriba/izquierda.
|
|
' PDF tiene origen abajo/izquierda.
|
|
' ---------------------------------------------------------------------
|
|
|
|
Dim x As Single =
|
|
coordenadaXBitmap * FACTOR_BITMAP_A_PDF
|
|
|
|
Dim y As Single =
|
|
(altoFirmaBitmap -
|
|
coordenadaYBitmap -
|
|
altoTextoBitmap) *
|
|
FACTOR_BITMAP_A_PDF
|
|
|
|
|
|
Dim p As New Paragraph(
|
|
If(datosTexto.texto, String.Empty))
|
|
|
|
|
|
p.SetFont(font)
|
|
p.SetFontSize(tamanoFuente)
|
|
p.SetMargin(0)
|
|
p.SetPadding(0)
|
|
p.SetMultipliedLeading(1.0F)
|
|
|
|
|
|
' ---------------------------------------------------------------------
|
|
' COLOR
|
|
' ---------------------------------------------------------------------
|
|
|
|
If datosTexto.colorTexto IsNot Nothing Then
|
|
|
|
p.SetFontColor(
|
|
ObtenerColorPdf(datosTexto.colorTexto))
|
|
|
|
End If
|
|
|
|
|
|
' ---------------------------------------------------------------------
|
|
' ESTILO
|
|
' ---------------------------------------------------------------------
|
|
|
|
If (datosTexto.estiloTexto And FontStyle.Bold) <> 0 Then
|
|
p.SetBold()
|
|
End If
|
|
|
|
If (datosTexto.estiloTexto And FontStyle.Italic) <> 0 Then
|
|
p.SetItalic()
|
|
End If
|
|
|
|
|
|
' ---------------------------------------------------------------------
|
|
' ROTACIÓN
|
|
' ---------------------------------------------------------------------
|
|
|
|
If Math.Abs(datosTexto.anguloRotacion) > 0.0001F Then
|
|
|
|
p.SetRotationAngle(
|
|
datosTexto.anguloRotacion *
|
|
Math.PI /
|
|
180.0)
|
|
|
|
End If
|
|
|
|
|
|
' ---------------------------------------------------------------------
|
|
' ALINEACIÓN
|
|
' ---------------------------------------------------------------------
|
|
|
|
Select Case datosTexto.AlineacionTexto
|
|
|
|
Case AlineamientoEnum.IZQUIERDA
|
|
|
|
canvas.ShowTextAligned(
|
|
p,
|
|
x,
|
|
y,
|
|
TextAlignment.LEFT)
|
|
|
|
|
|
Case AlineamientoEnum.CENTRO
|
|
|
|
canvas.ShowTextAligned(
|
|
p,
|
|
x,
|
|
y,
|
|
TextAlignment.CENTER)
|
|
|
|
|
|
Case AlineamientoEnum.DERECHA
|
|
|
|
canvas.ShowTextAligned(
|
|
p,
|
|
x,
|
|
y,
|
|
TextAlignment.RIGHT)
|
|
|
|
|
|
Case AlineamientoEnum.JUSTIFICADO
|
|
|
|
DibujarTextoJustificado(
|
|
canvas,
|
|
p,
|
|
x,
|
|
y,
|
|
anchoFirmaBitmap *
|
|
FACTOR_BITMAP_A_PDF)
|
|
|
|
|
|
Case AlineamientoEnum.JUSTIFICADO_ALL
|
|
|
|
DibujarTextoJustificado(
|
|
canvas,
|
|
p,
|
|
x,
|
|
y,
|
|
anchoFirmaBitmap *
|
|
FACTOR_BITMAP_A_PDF)
|
|
|
|
|
|
Case Else
|
|
|
|
canvas.ShowTextAligned(
|
|
p,
|
|
x,
|
|
y,
|
|
TextAlignment.LEFT)
|
|
|
|
End Select
|
|
|
|
End Sub
|
|
|
|
|
|
' =========================================================================
|
|
' IMAGEN
|
|
' =========================================================================
|
|
|
|
Private Shared Sub DibujarImagenFirma(
|
|
ByVal canvas As Canvas,
|
|
ByVal datosImagen As DatosImagenFirma,
|
|
ByVal anchoFirmaBitmap As Integer,
|
|
ByVal altoFirmaBitmap As Integer)
|
|
|
|
If datosImagen.Imagen Is Nothing OrElse
|
|
datosImagen.Imagen.Length = 0 Then
|
|
|
|
Return
|
|
|
|
End If
|
|
|
|
|
|
Dim imageData As ImageData =
|
|
ImageDataFactory.Create(
|
|
datosImagen.Imagen)
|
|
|
|
|
|
Dim imagen As New TSpdf.Layout.Element.Image(
|
|
imageData)
|
|
|
|
|
|
' ---------------------------------------------------------------------
|
|
' IMPORTANTE:
|
|
'
|
|
' En la versión Bitmap original se estaba usando el tamaño del Bitmap
|
|
' completo en DrawImage(), ignorando Ancho/Alto.
|
|
'
|
|
' Aquí utilizamos Ancho/Alto de DatosImagenFirma, que es lo lógico si
|
|
' esas propiedades representan el tamaño deseado de la imagen.
|
|
' ---------------------------------------------------------------------
|
|
|
|
Dim anchoPdf As Single =
|
|
datosImagen.Ancho *
|
|
FACTOR_BITMAP_A_PDF
|
|
|
|
Dim altoPdf As Single =
|
|
datosImagen.Alto *
|
|
FACTOR_BITMAP_A_PDF
|
|
|
|
|
|
If anchoPdf <= 0 OrElse altoPdf <= 0 Then
|
|
Return
|
|
End If
|
|
|
|
|
|
imagen.SetWidth(anchoPdf)
|
|
imagen.SetHeight(altoPdf)
|
|
|
|
|
|
' ---------------------------------------------------------------------
|
|
' Coordenadas equivalentes a calcularX/calcularY.
|
|
' ---------------------------------------------------------------------
|
|
|
|
Dim coordenadaXBitmap As Integer =
|
|
CalcularX(
|
|
datosImagen.PosicionX,
|
|
datosImagen.PosicionY,
|
|
datosImagen.PosicionEsquina,
|
|
anchoFirmaBitmap,
|
|
altoFirmaBitmap,
|
|
datosImagen.AnguloRotacion,
|
|
datosImagen.Ancho,
|
|
datosImagen.Alto)
|
|
|
|
|
|
Dim coordenadaYBitmap As Integer =
|
|
CalcularY(
|
|
datosImagen.PosicionY,
|
|
datosImagen.PosicionX,
|
|
datosImagen.PosicionEsquina,
|
|
anchoFirmaBitmap,
|
|
altoFirmaBitmap,
|
|
datosImagen.AnguloRotacion,
|
|
datosImagen.Ancho,
|
|
datosImagen.Alto)
|
|
|
|
|
|
Dim x As Single =
|
|
coordenadaXBitmap *
|
|
FACTOR_BITMAP_A_PDF
|
|
|
|
Dim y As Single =
|
|
(altoFirmaBitmap -
|
|
coordenadaYBitmap -
|
|
datosImagen.Alto) *
|
|
FACTOR_BITMAP_A_PDF
|
|
|
|
|
|
imagen.SetFixedPosition(x, y)
|
|
|
|
|
|
' ---------------------------------------------------------------------
|
|
' ROTACIÓN
|
|
' ---------------------------------------------------------------------
|
|
|
|
If Math.Abs(datosImagen.AnguloRotacion) > 0.0001 Then
|
|
|
|
imagen.SetRotationAngle(
|
|
datosImagen.AnguloRotacion *
|
|
Math.PI /
|
|
180.0)
|
|
|
|
End If
|
|
|
|
|
|
canvas.Add(imagen)
|
|
|
|
End Sub
|
|
|
|
|
|
' =========================================================================
|
|
' FUENTES
|
|
' =========================================================================
|
|
|
|
Private Shared Function ObtenerFuentePdf(
|
|
ByVal nombreFuente As String,
|
|
ByVal listadoFuentes As List(Of DatosFuente)) As PdfFont
|
|
|
|
If String.IsNullOrWhiteSpace(nombreFuente) Then
|
|
|
|
Return PdfFontFactory.CreateFont(
|
|
StandardFonts.HELVETICA)
|
|
|
|
End If
|
|
|
|
|
|
Select Case nombreFuente.ToUpperInvariant()
|
|
|
|
Case "HELVETICA"
|
|
|
|
Return PdfFontFactory.CreateFont(
|
|
StandardFonts.HELVETICA,
|
|
PdfEncodings.WINANSI)
|
|
|
|
|
|
Case "HELVETICA_BOLD"
|
|
|
|
Return PdfFontFactory.CreateFont(
|
|
StandardFonts.HELVETICA_BOLD,
|
|
PdfEncodings.WINANSI)
|
|
|
|
|
|
Case "HELVETICA_OBLIQUE"
|
|
|
|
Return PdfFontFactory.CreateFont(
|
|
StandardFonts.HELVETICA_OBLIQUE,
|
|
PdfEncodings.WINANSI)
|
|
|
|
|
|
Case "HELVETICA_BOLDOBLIQUE"
|
|
|
|
Return PdfFontFactory.CreateFont(
|
|
StandardFonts.HELVETICA_BOLDOBLIQUE,
|
|
PdfEncodings.WINANSI)
|
|
|
|
|
|
Case "TIMES_ROMAN"
|
|
|
|
Return PdfFontFactory.CreateFont(
|
|
StandardFonts.TIMES_ROMAN,
|
|
PdfEncodings.WINANSI)
|
|
|
|
|
|
Case "TIMES_BOLD"
|
|
|
|
Return PdfFontFactory.CreateFont(
|
|
StandardFonts.TIMES_BOLD,
|
|
PdfEncodings.WINANSI)
|
|
|
|
|
|
Case "TIMES_ITALIC"
|
|
|
|
Return PdfFontFactory.CreateFont(
|
|
StandardFonts.TIMES_ITALIC,
|
|
PdfEncodings.WINANSI)
|
|
|
|
|
|
Case "TIMES_BOLDITALIC"
|
|
|
|
Return PdfFontFactory.CreateFont(
|
|
StandardFonts.TIMES_BOLDITALIC,
|
|
PdfEncodings.WINANSI)
|
|
|
|
|
|
Case "COURIER"
|
|
|
|
Return PdfFontFactory.CreateFont(
|
|
StandardFonts.COURIER,
|
|
PdfEncodings.WINANSI)
|
|
|
|
|
|
Case "COURIER_BOLD"
|
|
|
|
Return PdfFontFactory.CreateFont(
|
|
StandardFonts.COURIER_BOLD,
|
|
PdfEncodings.WINANSI)
|
|
|
|
|
|
Case "COURIER_OBLIQUE"
|
|
|
|
Return PdfFontFactory.CreateFont(
|
|
StandardFonts.COURIER_OBLIQUE,
|
|
PdfEncodings.WINANSI)
|
|
|
|
|
|
Case "COURIER_BOLDOBLIQUE"
|
|
|
|
Return PdfFontFactory.CreateFont(
|
|
StandardFonts.COURIER_BOLDOBLIQUE,
|
|
PdfEncodings.WINANSI)
|
|
|
|
|
|
Case "SYMBOL"
|
|
|
|
Return PdfFontFactory.CreateFont(
|
|
StandardFonts.SYMBOL)
|
|
|
|
|
|
Case "ZAPFDINGBATS"
|
|
|
|
Return PdfFontFactory.CreateFont(
|
|
StandardFonts.ZAPFDINGBATS)
|
|
|
|
End Select
|
|
|
|
|
|
' ---------------------------------------------------------------------
|
|
' Fuente personalizada incluida en DatosFuente.
|
|
' ---------------------------------------------------------------------
|
|
|
|
If listadoFuentes IsNot Nothing Then
|
|
|
|
Dim fuente As DatosFuente =
|
|
listadoFuentes.FirstOrDefault(
|
|
Function(f)
|
|
Return f IsNot Nothing AndAlso
|
|
String.Equals(
|
|
f.NombreFuente,
|
|
nombreFuente,
|
|
StringComparison.OrdinalIgnoreCase)
|
|
End Function)
|
|
|
|
|
|
If fuente IsNot Nothing AndAlso
|
|
fuente.memoryFont IsNot Nothing AndAlso
|
|
fuente.memoryFont.Length > 0 Then
|
|
|
|
Return PdfFontFactory.CreateFont(
|
|
fuente.memoryFont,
|
|
PdfEncodings.IDENTITY_H)
|
|
|
|
End If
|
|
|
|
End If
|
|
|
|
|
|
Throw New Exception(
|
|
"Fuente " & nombreFuente & " no encontrada")
|
|
|
|
End Function
|
|
|
|
|
|
' =========================================================================
|
|
' COLOR
|
|
' =========================================================================
|
|
|
|
Private Shared Function ObtenerColorPdf(
|
|
ByVal brush As Brush) As TSpdf.Kernel.Colors.Color
|
|
|
|
If brush Is Nothing Then
|
|
Return ColorConstants.BLACK
|
|
End If
|
|
|
|
|
|
If TypeOf brush Is SolidBrush Then
|
|
|
|
Dim solidBrush As SolidBrush =
|
|
DirectCast(brush, SolidBrush)
|
|
|
|
|
|
Return New DeviceRgb(
|
|
solidBrush.Color.R,
|
|
solidBrush.Color.G,
|
|
solidBrush.Color.B)
|
|
|
|
End If
|
|
|
|
|
|
' Para otros tipos de Brush no existe una equivalencia directa sencilla
|
|
' en TSpdf. Se utiliza negro como valor por defecto.
|
|
|
|
Return ColorConstants.BLACK
|
|
|
|
End Function
|
|
|
|
|
|
' =========================================================================
|
|
' ALTURA DE TEXTO
|
|
' =========================================================================
|
|
|
|
Private Shared Function ObtenerAltoTexto(
|
|
ByVal font As PdfFont,
|
|
ByVal fontSize As Single) As Single
|
|
|
|
Try
|
|
|
|
Dim descriptor = font.GetFontProgram.GetFontMetrics
|
|
|
|
Dim ascender As Single =
|
|
descriptor.GetTypoAscender()
|
|
|
|
Dim descender As Single =
|
|
descriptor.GetTypoDescender()
|
|
|
|
|
|
If ascender > descender Then
|
|
|
|
Return (ascender - descender) /
|
|
1000.0F *
|
|
fontSize
|
|
|
|
End If
|
|
|
|
Catch
|
|
' Usamos el fallback.
|
|
End Try
|
|
|
|
|
|
Return fontSize
|
|
|
|
End Function
|
|
|
|
|
|
' =========================================================================
|
|
' JUSTIFICADO
|
|
' =========================================================================
|
|
'
|
|
' La implementación GDI+ tenía DrawJustifiedString(). Como esa función no
|
|
' está disponible aquí, para JUSTIFICADO/JUSTIFICADO_ALL se utiliza una
|
|
' caja cuyo ancho es el disponible y iText se encarga de la alineación.
|
|
'
|
|
' Si tu DrawJustifiedString() hace un tratamiento especial de espacios,
|
|
' habría que reproducirlo aparte.
|
|
'
|
|
' =========================================================================
|
|
|
|
Private Shared Sub DibujarTextoJustificado(
|
|
ByVal canvas As Canvas,
|
|
ByVal paragraph As Paragraph,
|
|
ByVal x As Single,
|
|
ByVal y As Single,
|
|
ByVal anchoDisponible As Single)
|
|
|
|
Dim tabla As New TSpdf.Layout.Element.Table(
|
|
New Single() {anchoDisponible})
|
|
|
|
tabla.SetFixedPosition(
|
|
x,
|
|
y,
|
|
anchoDisponible)
|
|
|
|
tabla.SetBorder(
|
|
TSpdf.Layout.Borders.Border.NO_BORDER)
|
|
|
|
tabla.SetPadding(0)
|
|
tabla.SetMargin(0)
|
|
|
|
Dim celda As New TSpdf.Layout.Element.Cell()
|
|
|
|
celda.SetBorder(
|
|
TSpdf.Layout.Borders.Border.NO_BORDER)
|
|
|
|
celda.SetPadding(0)
|
|
celda.SetMargin(0)
|
|
|
|
celda.SetTextAlignment(
|
|
TextAlignment.JUSTIFIED)
|
|
|
|
celda.Add(paragraph)
|
|
|
|
tabla.AddCell(celda)
|
|
|
|
canvas.Add(tabla)
|
|
|
|
End Sub
|
|
|
|
|
|
' =========================================================================
|
|
' CALCULAR X
|
|
' =========================================================================
|
|
'
|
|
' Conversión directa de tu calcularX().
|
|
' =========================================================================
|
|
|
|
Private Shared Function CalcularX(
|
|
ByVal coordenadaX As Integer,
|
|
ByVal coordenadaY As Integer,
|
|
ByVal posicionEsquina As EsquinaEnum,
|
|
ByVal anchoFirma As Integer,
|
|
ByVal altoFirma As Integer,
|
|
ByVal rotacionElemento As Single,
|
|
ByVal anchoElemento As Integer,
|
|
ByVal altoElemento As Integer) As Integer
|
|
|
|
Dim ejeX As Integer = 0
|
|
|
|
Dim rotacion As Integer =
|
|
NormalizarRotacion(rotacionElemento)
|
|
|
|
|
|
Select Case posicionEsquina
|
|
|
|
Case EsquinaEnum.SUPERIOR_IZQUIERDA
|
|
|
|
Select Case rotacion
|
|
|
|
Case 90
|
|
ejeX = coordenadaY
|
|
|
|
Case 180
|
|
ejeX = -coordenadaX - anchoElemento
|
|
|
|
Case 270
|
|
ejeX = -coordenadaY - anchoElemento
|
|
|
|
Case Else
|
|
ejeX = coordenadaX
|
|
|
|
End Select
|
|
|
|
|
|
Case EsquinaEnum.INFERIOR_IZQUIERDA
|
|
|
|
Select Case rotacion
|
|
|
|
Case 90
|
|
ejeX =
|
|
altoFirma -
|
|
(coordenadaY + anchoElemento)
|
|
|
|
Case 180
|
|
ejeX =
|
|
-coordenadaX -
|
|
anchoElemento
|
|
|
|
Case 270
|
|
ejeX =
|
|
coordenadaY -
|
|
altoFirma
|
|
|
|
Case Else
|
|
ejeX = coordenadaX
|
|
|
|
End Select
|
|
|
|
|
|
Case EsquinaEnum.SUPERIOR_DERECHA
|
|
|
|
Select Case rotacion
|
|
|
|
Case 90
|
|
ejeX = coordenadaY
|
|
|
|
Case 180
|
|
ejeX =
|
|
-anchoFirma +
|
|
coordenadaX
|
|
|
|
Case 270
|
|
ejeX =
|
|
-coordenadaY -
|
|
anchoElemento
|
|
|
|
Case Else
|
|
ejeX =
|
|
anchoFirma -
|
|
(anchoElemento + coordenadaX)
|
|
|
|
End Select
|
|
|
|
|
|
Case EsquinaEnum.INFERIOR_DERECHA
|
|
|
|
Select Case rotacion
|
|
|
|
Case 90
|
|
ejeX =
|
|
altoFirma -
|
|
(coordenadaY + anchoElemento)
|
|
|
|
Case 180
|
|
ejeX =
|
|
-anchoFirma +
|
|
coordenadaX
|
|
|
|
Case 270
|
|
ejeX =
|
|
coordenadaY -
|
|
altoFirma
|
|
|
|
Case Else
|
|
ejeX =
|
|
anchoFirma -
|
|
(anchoElemento + coordenadaX)
|
|
|
|
End Select
|
|
|
|
End Select
|
|
|
|
|
|
Return ejeX
|
|
|
|
End Function
|
|
|
|
|
|
' =========================================================================
|
|
' CALCULAR Y
|
|
' =========================================================================
|
|
'
|
|
' Conversión directa de tu calcularY().
|
|
' =========================================================================
|
|
|
|
Private Shared Function CalcularY(
|
|
ByVal coordenadaY As Integer,
|
|
ByVal coordenadaX As Integer,
|
|
ByVal posicionEsquina As EsquinaEnum,
|
|
ByVal anchoFirma As Integer,
|
|
ByVal altoFirma As Integer,
|
|
ByVal rotacionElemento As Single,
|
|
ByVal anchoElemento As Integer,
|
|
ByVal altoElemento As Integer) As Integer
|
|
|
|
Dim ejeY As Integer = 0
|
|
|
|
Dim rotacion As Integer =
|
|
NormalizarRotacion(rotacionElemento)
|
|
|
|
|
|
Select Case posicionEsquina
|
|
|
|
Case EsquinaEnum.SUPERIOR_IZQUIERDA
|
|
|
|
Select Case rotacion
|
|
|
|
Case 90
|
|
ejeY =
|
|
-coordenadaX -
|
|
altoElemento
|
|
|
|
Case 180
|
|
ejeY =
|
|
-coordenadaY -
|
|
altoElemento
|
|
|
|
Case 270
|
|
ejeY = coordenadaX
|
|
|
|
Case Else
|
|
ejeY = coordenadaY
|
|
|
|
End Select
|
|
|
|
|
|
Case EsquinaEnum.INFERIOR_IZQUIERDA
|
|
|
|
Select Case rotacion
|
|
|
|
Case 90
|
|
ejeY =
|
|
-coordenadaX -
|
|
altoElemento
|
|
|
|
Case 180
|
|
ejeY =
|
|
-altoFirma +
|
|
coordenadaY
|
|
|
|
Case 270
|
|
ejeY = coordenadaX
|
|
|
|
Case Else
|
|
ejeY =
|
|
altoFirma -
|
|
(altoElemento + coordenadaY)
|
|
|
|
End Select
|
|
|
|
|
|
Case EsquinaEnum.SUPERIOR_DERECHA
|
|
|
|
Select Case rotacion
|
|
|
|
Case 90
|
|
ejeY =
|
|
coordenadaX -
|
|
anchoFirma
|
|
|
|
Case 180
|
|
ejeY =
|
|
-coordenadaY -
|
|
altoElemento
|
|
|
|
Case 270
|
|
ejeY =
|
|
anchoFirma -
|
|
(altoElemento + coordenadaX)
|
|
|
|
Case Else
|
|
ejeY = coordenadaY
|
|
|
|
End Select
|
|
|
|
|
|
Case EsquinaEnum.INFERIOR_DERECHA
|
|
|
|
Select Case rotacion
|
|
|
|
Case 90
|
|
ejeY =
|
|
coordenadaX -
|
|
anchoFirma
|
|
|
|
Case 180
|
|
ejeY =
|
|
-altoFirma +
|
|
coordenadaY
|
|
|
|
Case 270
|
|
ejeY =
|
|
anchoFirma -
|
|
(altoElemento + coordenadaX)
|
|
|
|
Case Else
|
|
ejeY =
|
|
altoFirma -
|
|
(altoElemento + coordenadaY)
|
|
|
|
End Select
|
|
|
|
End Select
|
|
|
|
|
|
Return ejeY
|
|
|
|
End Function
|
|
|
|
|
|
' =========================================================================
|
|
' ROTACIÓN
|
|
' =========================================================================
|
|
|
|
Private Shared Function NormalizarRotacion(
|
|
ByVal rotacion As Single) As Integer
|
|
|
|
' Tu código original solamente contemplaba 0, 90, 180 y 270.
|
|
' Mantenemos ese comportamiento.
|
|
|
|
Dim resultado As Integer =
|
|
CInt(Math.Round(rotacion / 90.0R) * 90)
|
|
|
|
resultado =
|
|
resultado Mod 360
|
|
|
|
If resultado < 0 Then
|
|
resultado += 360
|
|
End If
|
|
|
|
Return resultado
|
|
|
|
End Function
|
|
|
|
End Class
|