Files
TSpdfUtils/TSPdfUtils/FirmaCanvas.vb

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