Files
ADM2/B4A/cPDF.bas
2025-12-26 13:51:14 -06:00

1003 lines
47 KiB
QBasic

B4J=true
Group=Default Group
ModulesStructureVersion=1
Type=Class
Version=9.8
@EndOfDesignText@
'releases
'0.1 : first version
'0.2 : add support fo image PNG with colorspace Indexed (palette)
'0.3 : add raw command to enter PDF command directly
'0.4 : add strikeThrough style for font
' add constants for font style
' add constants for colors
' add methods gMultilineTextSize and outTextFlow
' add setter/getter sX, sY, sY and gY to get/set the current position
'0.5 : add suport for all images type. image is convert to PNG before inserted. a temp file is created, insert into the pdf and deleted
'0.6 : add justify align
Sub Class_Globals
Type TPDFContext(fX As Double,fY As Double, _
fFontFamily As String,fFontSize As Double,fFontStyle As Int,fFontColor(3) As Double, _
fDrawWidth As Double,fDrawColor(3) As Double,fFillColor(3) As Double)
Type TPDFPage(fWidth As Double,fHeight As Double,fBuffer As String)
Type TPDFImageInfo(fError As Int,fWidth As Int,fHeight As Int,fBPP As Int,fColorSpace As Int,fCompression As Int,fFilter As Int,fInterlacing As Int,fData() As Byte,fPalette() As Byte)
Type TPDFFontInfo(fCharsWidths(256) As Int,fYUnderline As Int,fHUnderline As Int,fYStrikeThrough As Int,fHStrikeThrough As Int,fYBottom As Int,fYTop As Int)
Type TPDFTextSize(fWidth As Double,fTop As Double,fBottom As Double,fHeight As Double)
Type TPDFMultilineTextSize(fWidth As Double,fHeight As Double,fParagraphs As List)
'use to convert image to png
Private fXUI As XUI
'encoding file
Private const fTextEncoding As String="cp1252"
'versions
Private fLibVersion As String
Private fPDFVersion As String
'factor between user unit and pt
Private fUnitFactor As Double
'current context
Private fContext As TPDFContext
'properties of PDF document
Private fProperties As Map
'list of objects in the PDF
Private fObjs As List
'current page
Private fPage As Int
'liste of pages (TPDFPage)
Private fPages As List
'list of used images in the document, each item in a map (path,imageinfo)
Private fUsedImages As List
'list of used fonts in the document
Private fUsedFonts As List
'font info for all the stadards fonts (TPDFFontInfo)
Private fFontsInfos As Map
'constants for page size
Public const pageSizeA3PortraitWidth As Double=-841.89
Public const pageSizeA3PortraitHeight As Double=-1190.55
' Public const pageSizeA4PortraitWidth As Double=-595.28
' Public const pageSizeA4PortraitHeight As Double=-841.89
Public const pageSizeA4PortraitWidth As Double=-370
Public const pageSizeA4PortraitHeight As Double=-72
Public const pageSizeA5PortraitWidth As Double=-420.94
Public const pageSizeA5PortraitHeight As Double=-595.28
Public const pageSizeLetterPortraitWidth As Double=-612
Public const pageSizeLetterPortraitHeight As Double=-792
Public const pageSizeLegalPortraitWidth As Double=-612
Public const pageSizeLegalPortraitHeight As Double=-1008
Public const pageSizeA3LandscapeWidth As Double=-1190.55
Public const pageSizeA3LandscapeHeight As Double=-841.89
Public const pageSizeA4LandscapeWidth As Double=-841.89
Public const pageSizeA4LandscapeHeight As Double=-595.28
Public const pageSizeA5LandscapeWidth As Double=-595.28
Public const pageSizeA5LandscapeHeight As Double=-420.94
Public const pageSizeLetterLandscapeWidth As Double=-792
Public const pageSizeLetterLandscapeHeight As Double=-612
Public const pageSizeLegalLandscapeWidth As Double=-1008
Public const pageSizeLegalLandscapeHeight As Double=-612
'constants for standards fonts
Public const fontCourier As String="Courier"
Public const fontHelvetica As String="Helvetica"
Public const fontTimes As String="Times"
Public const fontSymbol As String="Symbol"
Public const fontZapfdingbats As String="zapfdingbats"
'constants for fonts styles
Public const fontNormal As Int=0
Public const fontBold As Int=1
Public const fontItalic As Int=2
Public const fontUnderline As Int=4
Public const fontStrikeThrough As Int=8
'constants for properties
Public const PropertyAuthor As String="Author"
Public const PropertyCreator As String="Creator"
Public const PropertyProducer As String="Producer"
Public const PropertyTitle As String="Title"
Public const PropertySubject As String="Subject"
Public const PropertyKeywords As String="Keywords"
'constants for rectangle style
Public const RectangleBorderOnly As String="S"
Public const RectangleFillOnly As String="f"
Public const RectangleBorderAndFill As String="B"
'constants for text align
Public const AlignLeft As String="L"
Public const AlignCenter As String="C"
Public const AlignRight As String="R"
Public const AlignJusify As String="J"
'constants for compression
Public const CompressAlways As Int=0
Public const CompressIfSmaller As Int=1
Public const CompressNever As Int=2
Public const colorBlack(3) As Double=Array As Double(0,0,0)
Public const colorWhite(3) As Double=Array As Double(1,1,1)
Public const colorGray(3) As Double=Array As Double(0.5,0.5,0.5)
Public const colorRed(3) As Double=Array As Double(1,0,0)
Public const colorGreen(3) As Double=Array As Double(0,1,0)
Public const colorBlue(3) As Double=Array As Double(0,0,1)
End Sub
'initialize and set unit to use : mm, cm, pt, in
Public Sub Initialize(aunit As String) As cPDF
fLibVersion="0.6"
fPDFVersion="1.3"
sUnit(aunit)
fProperties=CreateMap("Producer":"B4XPDF","CreationDate":gCreationDate,"ModDate":gCreationDate)
fContext.Initialize
fObjs.Initialize
fPages.Initialize
fPage=0
fUsedImages.Initialize
fUsedFonts.Initialize
initFontsInfos
Return Me
End Sub
private Sub createFontInfo(acharsWidths() As Int,ayunderline As Int,ahunderline As Int,aystrikethrough As Int,ahstrikethrough As Int,aytop As Int,aybottom As Int) As TPDFFontInfo
Dim fi As TPDFFontInfo
fi.Initialize
'width of each 256 chars
fi.fCharsWidths=acharsWidths
'bottom of underline rectangle
fi.fYUnderline=ayunderline
'height of underine rectangle
fi.fHUnderline=ahunderline
'bottom of strikethrough rectangle
fi.fYStrikeThrough=aystrikethrough
'height of strikethrough rectangle
fi.fHStrikeThrough=ahstrikethrough
'distance between baseline and top
fi.fYTop=aytop
'distance between baseline and bottom
fi.fybottom=aybottom
Return fi
End Sub
private Sub initFontsInfos
fFontsInfos.Initialize
fFontsInfos.Put(calcFontKey("Courier",fontNormal),createFontInfo(Array As Int(600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600),-60,40,220,40,800,-230))
fFontsInfos.Put(calcFontKey("Courier",fontBold),createFontInfo(Array As Int(600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600),-60,40,220,40,800,-230))
fFontsInfos.Put(calcFontKey("Courier",fontItalic),createFontInfo(Array As Int(600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600),-60,40,220,40,800,-230))
fFontsInfos.Put(calcFontKey("Courier",fontBold+fontItalic),createFontInfo(Array As Int(600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600,600),-60,40,220,40,800,-230))
fFontsInfos.Put(calcFontKey("Helvetica",fontNormal),createFontInfo(Array As Int(278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,355,556,556,889,667,191,333,333,389,584,278,333,278,278,556,556,556,556,556,556,556,556,556,556,278,278,584,584,584,556,1015,667,667,722,722,667,611,778,722,278,500,667,556,833,722,778,667,778,722,667,611,722,667,944,667,667,611,278,278,278,469,556,333,556,556,500,556,556,278,556,556,222,222,500,222,833,556,556,556,556,333,500,278,556,500,722,500,500,500,334,260,334,584,350,556,350,222,556,333,1000,556,556,333,1000,667,333,1000,350,611,350,350,222,222,333,333,350,556,1000,333,1000,500,333,944,350,500,667,278,333,556,556,556,556,260,556,333,737,370,556,584,333,737,333,400,584,333,333,333,556,537,278,333,333,365,556,834,834,834,611,667,667,667,667,667,667,1000,722,667,667,667,667,278,278,278,278,722,722,778,778,778,778,778,584,778,722,722,722,722,667,667,611,556,556,556,556,556,556,889,500,556,556,556,556,278,278,278,278,556,556,556,556,556,556,556,584,611,556,556,556,556,500,556,500),-60,40,220,40,800,-230))
fFontsInfos.Put(calcFontKey("Helvetica",fontBold),createFontInfo(Array As Int(278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,333,474,556,556,889,722,238,333,333,389,584,278,333,278,278,556,556,556,556,556,556,556,556,556,556,333,333,584,584,584,611,975,722,722,722,722,667,611,778,722,278,556,722,611,833,722,778,667,778,722,667,611,722,667,944,667,667,611,333,278,333,584,556,333,556,611,556,611,556,333,611,611,278,278,556,278,889,611,611,611,611,389,556,333,611,556,778,556,556,500,389,280,389,584,350,556,350,278,556,500,1000,556,556,333,1000,667,333,1000,350,611,350,350,278,278,500,500,350,556,1000,333,1000,556,333,944,350,500,667,278,333,556,556,556,556,280,556,333,737,370,556,584,333,737,333,400,584,333,333,333,611,556,278,333,333,365,556,834,834,834,611,722,722,722,722,722,722,1000,722,667,667,667,667,278,278,278,278,722,722,778,778,778,778,778,584,778,722,722,722,722,667,667,611,556,556,556,556,556,556,889,556,556,556,556,556,278,278,278,278,611,611,611,611,611,611,611,584,611,611,611,611,611,556,611,556),-60,40,220,40,800,-230))
fFontsInfos.Put(calcFontKey("Helvetica",fontItalic),createFontInfo(Array As Int(278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,355,556,556,889,667,191,333,333,389,584,278,333,278,278,556,556,556,556,556,556,556,556,556,556,278,278,584,584,584,556,1015,667,667,722,722,667,611,778,722,278,500,667,556,833,722,778,667,778,722,667,611,722,667,944,667,667,611,278,278,278,469,556,333,556,556,500,556,556,278,556,556,222,222,500,222,833,556,556,556,556,333,500,278,556,500,722,500,500,500,334,260,334,584,350,556,350,222,556,333,1000,556,556,333,1000,667,333,1000,350,611,350,350,222,222,333,333,350,556,1000,333,1000,500,333,944,350,500,667,278,333,556,556,556,556,260,556,333,737,370,556,584,333,737,333,400,584,333,333,333,556,537,278,333,333,365,556,834,834,834,611,667,667,667,667,667,667,1000,722,667,667,667,667,278,278,278,278,722,722,778,778,778,778,778,584,778,722,722,722,722,667,667,611,556,556,556,556,556,556,889,500,556,556,556,556,278,278,278,278,556,556,556,556,556,556,556,584,611,556,556,556,556,500,556,500),-60,40,220,40,800,-230))
fFontsInfos.Put(calcFontKey("Helvetica",fontBold+fontItalic),createFontInfo(Array As Int(278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,333,474,556,556,889,722,238,333,333,389,584,278,333,278,278,556,556,556,556,556,556,556,556,556,556,333,333,584,584,584,611,975,722,722,722,722,667,611,778,722,278,556,722,611,833,722,778,667,778,722,667,611,722,667,944,667,667,611,333,278,333,584,556,333,556,611,556,611,556,333,611,611,278,278,556,278,889,611,611,611,611,389,556,333,611,556,778,556,556,500,389,280,389,584,350,556,350,278,556,500,1000,556,556,333,1000,667,333,1000,350,611,350,350,278,278,500,500,350,556,1000,333,1000,556,333,944,350,500,667,278,333,556,556,556,556,280,556,333,737,370,556,584,333,737,333,400,584,333,333,333,611,556,278,333,333,365,556,834,834,834,611,722,722,722,722,722,722,1000,722,667,667,667,667,278,278,278,278,722,722,778,778,778,778,778,584,778,722,722,722,722,667,667,611,556,556,556,556,556,556,889,556,556,556,556,556,278,278,278,278,611,611,611,611,611,611,611,584,611,611,611,611,611,556,611,556),-60,40,220,40,800,-230))
fFontsInfos.Put(calcFontKey("Times",fontNormal),createFontInfo(Array As Int(250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,333,408,500,500,833,778,180,333,333,500,564,250,333,250,278,500,500,500,500,500,500,500,500,500,500,278,278,564,564,564,444,921,722,667,667,722,611,556,722,722,333,389,722,611,889,722,722,556,722,667,556,611,722,722,944,722,722,611,333,278,333,469,500,333,444,500,444,500,444,333,500,500,278,278,500,278,778,500,500,500,500,333,389,278,500,500,722,500,500,444,480,200,480,541,350,500,350,333,500,444,1000,500,500,333,1000,556,333,889,350,611,350,350,333,333,444,444,350,500,1000,333,980,389,333,722,350,444,722,250,333,500,500,500,500,200,500,333,760,276,500,564,333,760,333,400,564,300,300,333,500,453,250,333,300,310,500,750,750,750,444,722,722,722,722,722,722,889,667,611,611,611,611,333,333,333,333,722,722,722,722,722,722,722,564,722,722,722,722,722,722,556,500,444,444,444,444,444,444,667,444,444,444,444,444,278,278,278,278,500,500,500,500,500,500,500,564,500,500,500,500,500,500,500,500),-60,40,2200,40,800,-230))
fFontsInfos.Put(calcFontKey("Times",fontBold),createFontInfo(Array As Int(250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,333,555,500,500,1000,833,278,333,333,500,570,250,333,250,278,500,500,500,500,500,500,500,500,500,500,333,333,570,570,570,500,930,722,667,722,722,667,611,778,778,389,500,778,667,944,722,778,611,778,722,556,667,722,722,1000,722,722,667,333,278,333,581,500,333,500,556,444,556,444,333,500,556,278,333,556,278,833,556,500,556,556,444,389,333,556,500,722,500,500,444,394,220,394,520,350,500,350,333,500,500,1000,500,500,333,1000,556,333,1000,350,667,350,350,333,333,500,500,350,500,1000,333,1000,389,333,722,350,444,722,250,333,500,500,500,500,220,500,333,747,300,500,570,333,747,333,400,570,300,300,333,556,540,250,333,300,330,500,750,750,750,500,722,722,722,722,722,722,1000,722,667,667,667,667,389,389,389,389,722,722,778,778,778,778,778,570,778,722,722,722,722,722,611,556,500,500,500,500,500,500,722,444,444,444,444,444,278,278,278,278,500,556,500,500,500,500,500,570,500,556,556,556,556,500,556,500),-60,40,220,40,800,-230))
fFontsInfos.Put(calcFontKey("Times",fontItalic),createFontInfo(Array As Int(250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,333,420,500,500,833,778,214,333,333,500,675,250,333,250,278,500,500,500,500,500,500,500,500,500,500,333,333,675,675,675,500,920,611,611,667,722,611,611,722,722,333,444,667,556,833,667,722,611,722,611,500,556,722,611,833,611,556,556,389,278,389,422,500,333,500,500,444,500,444,278,500,500,278,278,444,278,722,500,500,500,500,389,389,278,500,444,667,444,444,389,400,275,400,541,350,500,350,333,500,556,889,500,500,333,1000,500,333,944,350,556,350,350,333,333,556,556,350,500,889,333,980,389,333,667,350,389,556,250,389,500,500,500,500,275,500,333,760,276,500,675,333,760,333,400,675,300,300,333,500,523,250,333,300,310,500,750,750,750,500,611,611,611,611,611,611,889,667,611,611,611,611,333,333,333,333,722,667,722,722,722,722,722,675,722,722,722,722,722,556,611,500,500,500,500,500,500,500,667,444,444,444,444,444,278,278,278,278,500,500,500,500,500,500,500,675,500,500,500,500,500,444,500,444),-60,40,220,40,800,-230))
fFontsInfos.Put(calcFontKey("Times",fontBold+fontItalic),createFontInfo(Array As Int(250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,389,555,500,500,833,778,278,333,333,500,570,250,333,250,278,500,500,500,500,500,500,500,500,500,500,333,333,570,570,570,500,832,667,667,667,722,667,667,722,778,389,500,667,611,889,722,722,611,722,667,556,611,722,667,889,667,611,611,333,278,333,570,500,333,500,500,444,500,444,333,500,556,278,278,500,278,778,556,500,500,500,389,389,278,556,444,667,500,444,389,348,220,348,570,350,500,350,333,500,500,1000,500,500,333,1000,556,333,944,350,611,350,350,333,333,500,500,350,500,1000,333,1000,389,333,722,350,389,611,250,389,500,500,500,500,220,500,333,747,266,500,606,333,747,333,400,570,300,300,333,576,500,250,333,300,300,500,750,750,750,500,667,667,667,667,667,667,944,667,667,667,667,667,389,389,389,389,722,722,722,722,722,722,722,570,722,722,722,722,722,611,611,500,500,500,500,500,500,500,722,444,444,444,444,444,278,278,278,278,500,556,500,500,500,500,500,570,500,556,556,556,556,444,500,444),-60,40,220,40,800,-230))
fFontsInfos.Put(calcFontKey("Symbol",fontNormal),createFontInfo(Array As Int(250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,250,333,713,500,549,833,778,439,333,333,500,549,250,549,250,278,500,500,500,500,500,500,500,500,500,500,278,278,549,549,549,444,549,722,667,722,612,611,763,603,722,333,631,722,686,889,722,722,768,741,556,592,611,690,439,768,645,795,611,333,863,333,658,500,500,631,549,549,494,439,521,411,603,329,603,549,549,576,521,549,549,521,549,603,439,576,713,686,493,686,494,480,200,480,549,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,750,620,247,549,167,713,500,753,753,753,753,1042,987,603,987,603,400,549,411,549,549,713,494,460,549,549,549,549,1000,603,1000,658,823,686,795,987,768,768,823,768,768,713,713,713,713,713,713,713,768,713,790,790,890,823,549,250,713,603,603,1042,987,603,987,603,494,329,790,790,786,713,384,384,384,384,384,384,494,494,494,494,0,329,274,686,686,686,384,384,384,384,384,384,494,494,494,0),-60,10,220,40,800,-230))
fFontsInfos.Put(calcFontKey("zapfdingbats",fontNormal),createFontInfo(Array As Int(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,278,974,961,974,980,719,789,790,791,690,960,939,549,855,911,933,911,945,974,755,846,762,761,571,677,763,760,759,754,494,552,537,577,692,786,788,788,790,793,794,816,823,789,841,823,833,816,831,923,744,723,749,790,792,695,776,768,792,759,707,708,682,701,826,815,789,789,707,687,696,689,786,787,713,791,785,791,873,761,762,762,759,759,892,892,788,784,438,138,277,415,392,392,668,668,0,390,390,317,317,276,276,509,509,410,410,234,234,334,334,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,732,544,544,910,667,760,760,776,595,694,626,788,788,788,788,788,788,788,788,788,788,788,788,788,788,788,788,788,788,788,788,788,788,788,788,788,788,788,788,788,788,788,788,788,788,788,788,788,788,788,788,894,838,1016,458,748,924,748,918,927,928,928,834,873,828,924,924,917,930,931,463,883,836,836,867,867,696,696,874,0,874,760,946,771,865,771,888,967,888,831,873,927,970,918,0),-60,40,220,40,800,-230))
End Sub
'set factor to convert user unit to pt
private Sub sUnit(aunit As String) As cPDF
Select aunit.ToLowerCase
Case "pt"
fUnitFactor=1.0
Case "mm"
fUnitFactor=72.0/25.4
Case "cm"
fUnitFactor=72.0/2.54
Case "in"
fUnitFactor=72.0
Case Else
fUnitFactor=1.0
End Select
Return Me
End Sub
'return current date and time in format D:YYYYMMDDHHMMSS±hh'mm'
private Sub gCreationDate As String
DateTime.DateFormat="yyyyMMdd"
DateTime.TimeFormat="hhmmss"
Dim z As String=NumberFormat2(DateTime.TimeZoneOffset,2,2,2,False)
z=IIf(z.SubString2(0,1)<>"-","+","") & z
z=z.SubString2(0,3) & "'" & z.SubString(4) & "'"
Return "D:" & DateTime.Date(DateTime.Now) & DateTime.Time(DateTime.Now) & z
End Sub
'all the ....write methods write a part of the pdf
private Sub headerWrite As Byte()
Dim bb As B4XBytesBuilder
bb.Initialize
bb.Append($"%PDF-${NumberFormat2(fPDFVersion,1,1,1,False)}${Chr(10)}"$.getBytes(fTextEncoding))
Return bb.toarray
End Sub
private Sub catalogWrite As Byte()
Dim bb As B4XBytesBuilder
bb.Initialize
bb.Append($"${fObjs.Size+1} 0 obj <</Type /Catalog /Pages 2 0 R>> endobj${Chr(10)}"$.getBytes(fTextEncoding))
fObjs.Add(bb.Length)
Return bb.toarray
End Sub
private Sub kidsWrite As Byte()
Dim bb As B4XBytesBuilder
bb.Initialize
bb.Append($"${fObjs.Size+1} 0 obj <</Type /Pages /Kids ["$.getbytes(fTextEncoding))
For i=1 To fPages.Size
bb.append($"${fObjs.Size+1+i} 0 R "$.getbytes(fTextEncoding))
Next
bb.Append($"] /Count ${fPages.size}>> endobj${Chr(10)}"$.getbytes(fTextEncoding))
fObjs.Add(bb.Length)
Return bb.ToArray
End Sub
private Sub pagesWrite As Byte()
Dim bb As B4XBytesBuilder
bb.Initialize
Dim r As Int=fObjs.Size+1+fPages.Size*2
For i=0 To fPages.Size-1
bb.append(pageWrite(i,r))
Next
Return bb.ToArray
End Sub
private Sub pageWrite(apage As Int,aresource As Int) As Byte()
Dim bb As B4XBytesBuilder
bb.Initialize
Dim p As TPDFPage=fPages.Get(apage)
bb.Append($"${fObjs.Size+1} 0 obj <</Type /Page /Parent 2 0 R /Resources ${aresource} 0 R /MediaBox [0 0 ${NumberFormat2(p.fwidth,1,2,2,False)} ${NumberFormat2(p.fheight,1,2,2,False)}] /Contents ${fObjs.Size+1+fPages.size} 0 R>> endobj${Chr(10)}"$.getbytes(fTextEncoding))
fObjs.Add(bb.Length)
Return bb.ToArray
End Sub
private Sub contentsWrite(acompress As Int) As Byte()
Dim bb As B4XBytesBuilder
bb.Initialize
For i=0 To fPages.Size-1
bb.append(contentWrite(acompress,i))
Next
Return bb.toarray
End Sub
private Sub contentWrite(acompress As Int,apage As Int) As Byte()
Dim const compress As String="/Filter/FlateDecode"
Dim bb As B4XBytesBuilder
bb.Initialize
Dim c() As Byte=fPages.Get(apage).As(TPDFPage).fBuffer.GetBytes(fTextEncoding)
Dim filter As String=""
If acompress<>CompressNever Then
Dim cs As CompressedStreams
Dim b() As Byte=cs.CompressBytes(c,"zlib")
If acompress=CompressAlways Or b.Length+compress.length<c.Length Then
c=b
filter=compress
End If
End If
bb.Append($"${fObjs.Size+1} 0 obj <<${filter}/Length ${c.length}>>${Chr(10)}stream${Chr(10)}"$.getbytes(fTextEncoding))
bb.Append(c)
bb.Append($"${Chr(10)}endstream${Chr(10)}endobj${Chr(10)}"$.getBytes(fTextEncoding))
fObjs.Add(bb.Length)
Return bb.ToArray
End Sub
private Sub resourcesWrite As Byte()
Dim bb As B4XBytesBuilder
bb.Initialize
bb.Append($"${fObjs.size+1} 0 obj << "$.getBytes(fTextEncoding))
bb.Append($"/ProcSet [/PDF /Text /ImageB /ImageC /ImageI] ${Chr(10)}"$.getBytes(fTextEncoding))
bb.append($"/Font << ${Chr(10)}"$.getBytes(fTextEncoding))
For i=0 To fUsedFonts.Size-1
bb.Append($"/F${i} ${fObjs.Size+1+i+1} 0 R "$.getBytes(fTextEncoding))
Next
bb.append($" >> ${Chr(10)}"$.getBytes(fTextEncoding))
bb.append($"/XObject << ${Chr(10)}"$.getBytes(fTextEncoding))
For i=0 To fUsedImages.Size-1
bb.Append($"/I${i} ${fObjs.Size+1+i+1+fUsedFonts.size} 0 R "$.getBytes(fTextEncoding))
Next
bb.append($" >> ${Chr(10)}"$.getBytes(fTextEncoding))
bb.Append($" >> endobj${Chr(10)}"$.getBytes(fTextEncoding))
fObjs.Add(bb.Length)
Return bb.ToArray
End Sub
private Sub fontsWrite As Byte()
Dim bb As B4XBytesBuilder
bb.Initialize
For i=0 To fUsedFonts.Size-1
bb.append(fontWrite(i))
Next
Return bb.ToArray
End Sub
private Sub fontWrite(afont As Int) As Byte()
Dim bb As B4XBytesBuilder
bb.Initialize
Dim c As String=fUsedFonts.Get(afont)
bb.Append($"${fObjs.Size+1} 0 obj << /Type /Font /Subtype /Type1 /BaseFont /${c} /Encoding /WinAnsiEncoding>> endobj${Chr(10)}"$.getbytes(fTextEncoding))
fObjs.Add(bb.Length)
Return bb.ToArray
End Sub
private Sub imagesWrite As Byte()
Dim bb As B4XBytesBuilder
bb.Initialize
For i=0 To fUsedImages.Size-1
bb.append(imageWrite(i))
Next
Return bb.toarray
End Sub
private Sub imageWrite(aimage As Int) As Byte()
Dim bb As B4XBytesBuilder
bb.Initialize
Dim ii As TPDFImageInfo=fUsedImages.Get(aimage).As(Map).Get("info")
bb.Append($"${fObjs.Size+1} 0 obj${Chr(10)}<</Type /XObject${Chr(10)}/Subtype /Image${Chr(10)}/Width ${ii.fWidth}${Chr(10)}/Height ${ii.fHeight}${Chr(10)}"$.getbytes(fTextEncoding))
Dim cs As String=Array As String("DeviceGray","Error","DeviceRGB","Indexed","DeviceGray","Error","DeviceRGB")(ii.fColorSpace)
If cs="Indexed" Then
bb.Append($"/ColorSpace [/Indexed /DeviceRGB ${NumberFormat2(ii.fPalette.Length/3-1,1,0,0,False)} ${fObjs.Size+2} 0 R]${Chr(10)}"$.getbytes(fTextEncoding))
Else
bb.Append($"/ColorSpace /DeviceRGB${Chr(10)}"$.getbytes(fTextEncoding))
End If
bb.Append($"/BitsPerComponent ${ii.fBPP}${Chr(10)}/Filter /FlateDecode${Chr(10)}/DecodeParms <</Predictor 15 /Colors ${IIf(cs="DeviceRGB",3,1)} /BitsPerComponent ${ii.fBPP} /Columns ${ii.fWidth}>>${Chr(10)}"$.getBytes(fTextEncoding))
bb.Append($"/Length ${ii.fData.length}>>${Chr(10)}stream${Chr(10)}"$.getBytes(fTextEncoding))
bb.Append(ii.fdata)
bb.Append($"${Chr(10)}endstream${Chr(10)}endobj${Chr(10)}"$.getBytes(fTextEncoding))
fObjs.Add(bb.Length)
If cs="Indexed" Then
bb.Append(paletteWrite(ii.fPalette))
End If
Return bb.ToArray
End Sub
private Sub paletteWrite(apalette() As Byte) As Byte()
Dim bb As B4XBytesBuilder
bb.Initialize
bb.Append($"${fObjs.Size+1} 0 obj${Chr(10)}<</Length ${apalette.Length}>>${Chr(10)}stream${Chr(10)}"$.getbytes(fTextEncoding))
bb.Append(apalette)
bb.Append($"${Chr(10)}endstream${Chr(10)}endobj${Chr(10)}"$.getBytes(fTextEncoding))
fObjs.Add(bb.Length)
Return bb.ToArray
End Sub
private Sub propertiesWrite As Byte()
Dim bb As B4XBytesBuilder
bb.Initialize
bb.Append($"${fObjs.Size+1} 0 obj <<${Chr(10)}"$.getBytes(fTextEncoding))
For Each k As String In fProperties.keys
bb.Append($"/${k}(${escapeText(fProperties.Get(k))})${Chr(10)}"$.getBytes(fTextEncoding))
Next
bb.Append($" >> endobj${Chr(10)}"$.getBytes(fTextEncoding))
fObjs.Add(bb.Length)
Return bb.ToArray
End Sub
private Sub refWrite(astart As Long,ageneration As Int,astatus As String) As Byte()
Dim bb As B4XBytesBuilder
bb.Initialize
bb.Append($"${NumberFormat2(astart,10,0,0,False)} ${NumberFormat2(ageneration,5,0,0,False)} ${astatus}${Chr(10)}"$.getBytes(fTextEncoding))
Return bb.ToArray
End Sub
private Sub xrefWrite As Byte()
Dim bb As B4XBytesBuilder
bb.Initialize
Dim s As Long=headerWrite.length
bb.Append($"xref${Chr(10)}"$.getBytes(fTextEncoding))
bb.Append($"0 ${fObjs.Size+1}${Chr(10)}"$.getBytes(fTextEncoding))
bb.Append(refWrite(0,65535,"f"))
For i=0 To fObjs.Size-1
Dim l As Int=fObjs.Get(i)
bb.Append(refWrite(s,0,"n"))
s=s+l
Next
Return bb.ToArray
End Sub
private Sub trailerWrite As Byte()
Dim bb As B4XBytesBuilder
bb.Initialize
Dim s As Long=headerWrite.length
For i=0 To fObjs.Size-1
Dim l As Int=fObjs.Get(i)
s=s+l
Next
bb.Append($"trailer <<${Chr(10)}"$.getBytes(fTextEncoding))
bb.Append($"/Size ${fObjs.Size+1}${Chr(10)}"$.getBytes(fTextEncoding))
bb.Append($"/Info ${fObjs.Size} 0 R${Chr(10)}"$.getBytes(fTextEncoding))
bb.Append($"/Root 1 0 R${Chr(10)}"$.getBytes(fTextEncoding))
bb.Append($">>${Chr(10)}"$.getBytes(fTextEncoding))
bb.Append($"startxref${Chr(10)}"$.getBytes(fTextEncoding))
bb.Append($"${s}${Chr(10)}"$.getBytes(fTextEncoding))
Return bb.ToArray
End Sub
private Sub footerWrite As Byte()
Return "%%EOF".GetBytes(fTextEncoding)
End Sub
'calc a unique key for a specific font (family ans style (bold, italic))
private Sub calcFontKey(afamily As String,astyle As Int) As String
If afamily="zapfdingbats" Or afamily="Symbol" Then
astyle=Bit.And(astyle,Bit.Not(Bit.Or(fontBold,fontItalic)))
End If
If Bit.And(astyle,fontBold)<>0 And Bit.And(astyle,fontItalic)<>0 Then
Return afamily & "-BoldOblique"
End If
If Bit.And(astyle,fontBold)<>0 Then
Return afamily & "-Bold"
End If
If Bit.And(astyle,fontItalic)<>0 Then
Return afamily & "-Oblique"
End If
If afamily="Times" Then
Return "Times-Roman"
End If
Return afamily
End Sub
'add content to a page
private Sub contentAdd(apage As Int,acontent As String)
Dim sb As StringBuilder
sb.Initialize
sb.Append(fPages.Get(apage).As(TPDFPage).fbuffer)
sb.Append(acontent)
fPages.Get(apage).As(TPDFPage).fbuffer=sb.ToString
End Sub
'escape characters ( ) \
private Sub escapeText(atext As String) As String
Return atext.Replace("\","\\").Replace("(","\(").Replace(")","\)")
End Sub
'initialize, set properties and return a TPDFPage
private Sub createPage(awidth As Double,aheight As Double,abuffer As String) As TPDFPage
Dim p As TPDFPage
p.Initialize
p.fWidth=awidth
p.fHeight=aheight
p.fBuffer=abuffer
Return p
End Sub
private Sub rafReadString(araf As RandomAccessFile,astart As Long,acount As Long) As String
Dim s As String
For i=0 To acount-1
s=s & Chr(araf.ReadUnsignedByte(astart+i))
Next
Return s
End Sub
'calc a unique key for a specific image (adir & afile)
private Sub imageKey(adir As String,afile As String) As String
Return adir & afile
End Sub
'parse png file
private Sub imageInfo(adir As String,afile As String) As TPDFImageInfo
Dim ii As TPDFImageInfo
Dim raf As RandomAccessFile
Dim rafpos As Long
Dim bbData As B4XBytesBuilder
Dim bbPalette As B4XBytesBuilder
bbData.Initialize
bbPalette.Initialize
ii.Initialize
ii.fError=0
raf.Initialize(adir,afile,True)
rafpos=0
'signture PNG
If rafReadString(raf,rafpos,8)<>Chr(137) & "PNG" & Chr(13) & Chr(10) & Chr(26) & Chr(10) Then
ii.fError=1
End If
rafpos=rafpos+8
'chunks (length, type, data, CRC)
Dim chunkLength As Int
Dim chunkType As String
Do While (ii.fError=0) And (chunkType<>"IEND")
chunkLength=raf.ReadInt(rafpos)
rafpos=rafpos+4
chunkType=rafReadString(raf,rafpos,4)
rafpos=rafpos+4
Select chunkType
'header
Case "IHDR"
ii.fWidth=raf.ReadInt(rafpos)
rafpos=rafpos+4
ii.fHeight=raf.ReadInt(rafpos)
rafpos=rafpos+4
ii.fBPP=raf.ReadUnsignedByte(rafpos)
If ii.fBPP>8 Then
ii.fError=2
End If
rafpos=rafpos+1
ii.fColorSpace=raf.ReadUnsignedByte(rafpos)
If ii.fColorSpace<>0 And ii.fColorSpace<>2 And ii.fColorSpace<>3 And ii.fColorSpace<>4 Then
ii.FError=3
End If
rafpos=rafpos+1
ii.FCompression=raf.ReadUnsignedByte(rafpos)
If ii.FCompression<>0 Then
ii.FError=4
End If
rafpos=rafpos+1
ii.FFilter=raf.ReadUnsignedByte(rafpos)
If ii.FFilter<>0 Then
ii.FError=5
End If
rafpos=rafpos+1
ii.FInterlacing=raf.ReadUnsignedByte(rafpos)
If ii.FInterlacing<>0 Then
ii.FError=6
End If
rafpos=rafpos+1
'data
Case "IDAT"
Dim b(chunkLength) As Byte
raf.ReadBytes(b,0,chunkLength,rafpos)
bbData.Append(b)
rafpos=rafpos+chunkLength
'palette
Case "PLTE"
Dim b(chunkLength) As Byte
raf.ReadBytes(b,0,chunkLength,rafpos)
bbPalette.Append(b)
rafpos=rafpos+chunkLength
'ignore other chunktype
Case Else
'skip Data
rafpos=rafpos+chunkLength
End Select
'skip CRC
rafpos=rafpos+4
Loop
raf.close
ii.fData=bbData.ToArray
ii.fPalette=bbPalette.ToArray
Return ii
End Sub
'save to stream
public Sub saveToStream(aoutputstream As OutputStream,acompress As Int) As cPDF
Dim b() As Byte=headerWrite
aoutputstream.WriteBytes(b,0,b.length)
Dim b() As Byte=catalogWrite
aoutputstream.WriteBytes(b,0,b.length)
Dim b() As Byte=kidsWrite
aoutputstream.WriteBytes(b,0,b.length)
Dim b() As Byte=pagesWrite
aoutputstream.WriteBytes(b,0,b.length)
Dim b() As Byte=contentsWrite(acompress)
aoutputstream.WriteBytes(b,0,b.length)
Dim b() As Byte=resourcesWrite
aoutputstream.WriteBytes(b,0,b.length)
Dim b() As Byte=fontsWrite
aoutputstream.WriteBytes(b,0,b.length)
Dim b() As Byte=imagesWrite
aoutputstream.WriteBytes(b,0,b.length)
Dim b() As Byte=propertiesWrite
aoutputstream.WriteBytes(b,0,b.length)
Dim b() As Byte=xrefWrite
aoutputstream.WriteBytes(b,0,b.length)
Dim b() As Byte=trailerWrite
aoutputstream.WriteBytes(b,0,b.length)
Dim b() As Byte=footerWrite
aoutputstream.WriteBytes(b,0,b.length)
Return Me
End Sub
'save pdf to a file
public Sub saveToFile(adir As String,afile As String,acompress As Int) As cPDF
File.Delete(adir,afile)
Dim outputstream As OutputStream=File.OpenOutput(adir,afile,False)
saveToStream(outputstream,acompress)
outputstream.Close
Return Me
End Sub
'return the index of the image with path=akey in fUSedImages
'return -1 if not found
private Sub findImageKey(akey As String) As Int
For i=0 To fUsedImages.Size-1
If fUsedImages.Get(i).As(Map).Get("path")=akey Then
Return i
End If
Next
Return -1
End Sub
'get the number of pages in the pdf
public Sub gPagesCount As Int
Return fPages.size
End Sub
'get the width of the page
'apage : 1 to n
public Sub gPageWidth(apage As Int) As Double
Return fPages.Get(apage-1).As(TPDFPage).fWidth/fUnitFactor
End Sub
'get the height of the page
'apage : 1 to n
public Sub gPageHeight(apage As Int) As Double
Return fPages.Get(apage-1).As(TPDFPage).fHeight/fUnitFactor
End Sub
'add a new page with width and height
'use predefined constants pageSize...
'or enter custom size (unit is unit defined in intialize method)
'the new page becomes the current page
public Sub pageAdd(awidth As Double,aheight As Double) As cPDF
fPages.Add(createPage(IIf(awidth<0,-awidth,awidth*fUnitFactor),IIf(aheight<0,-aheight,aheight*fUnitFactor),""))
fPage=fPages.Size-1
sFont(fContext.fFontFamily,fContext.fFontStyle,fContext.fFontSize,fContext.fFontColor)
sDrawColor(fContext.fDrawColor)
sDrawWidth(fContext.fDrawWidth)
sFillColor(fContext.fFillColor)
Return Me
End Sub
'get current page
public Sub gPage As Int
Return fPage+1
End Sub
'set current page
'apage : 1 to n
public Sub sPage(apage As Int) As cPDF
fPage=apage-1
Return Me
End Sub
'set draw color for line
'argb : Red Green Blue
'values from 0.000 to 1.000
public Sub sDrawColor(argb() As Double) As cPDF
fContext.fDrawColor=argb
contentAdd(fPage,$"${NumberFormat2(argb(0),1,3,3,False)} ${NumberFormat2(argb(1),1,3,3,False)} ${NumberFormat2(argb(2),1,3,3,False)} RG${Chr(10)}"$)
Return Me
End Sub
'set draw width for line
public Sub sDrawWidth(awidth As Double) As cPDF
fContext.fDrawWidth=awidth
contentAdd(fPage,$"${NumberFormat2(awidth*fUnitFactor,1,3,3,False)} w${Chr(10)}"$)
Return Me
End Sub
'set fill color
'argb : Red Green Blue
'values from 0.000 to 1.000
public Sub sFillColor(argb() As Double) As cPDF
fContext.fFillColor=argb
Return Me
End Sub
'set font
'family : Helvetica,Times,Courier,Symbol,zapfdingbats (use predefined constants font...)
'size in pts
'font uses cp-1252 windows encoding
public Sub sFont(afamily As String,astyle As Int,asize As Double,acolor() As Double) As cPDF
Dim k As String=calcFontKey(afamily,astyle)
Dim i As Int=fUsedFonts.IndexOf(k)
If i=-1 Then
fUsedFonts.Add(k)
i=fUsedFonts.Size-1
End If
contentAdd(fPage,$"BT /F${i} ${NumberFormat2(asize,1,3,3,False)} Tf ET${Chr(10)}"$)
fContext.fFontFamily=afamily
fContext.fFontStyle=astyle
fContext.fFontSize=asize
fContext.fFontColor=acolor
Return Me
End Sub
'draw text on current page at position ax,ay with current font and current text color
'(0,0) is bottom left of the page
'(w,h) if top right of the page
public Sub outText(ax As Double,ay As Double,atext As String) As cPDF
contentAdd(fPage,$"${NumberFormat2(fContext.fFontColor(0),1,3,3,False)} ${NumberFormat2(fContext.fFontColor(1),1,3,3,False)} ${NumberFormat2(fContext.fFontColor(2),1,3,3,False)} rg${Chr(10)}"$)
contentAdd(fPage,$"BT ${NumberFormat2(ax*fUnitFactor,1,3,3,False)} ${NumberFormat2(ay*fUnitFactor,1,3,3,False)} Td (${escapeText(atext)})Tj ET${Chr(10)}"$)
If Bit.And(fContext.fFontstyle,fontUnderline+fontStrikeThrough)<>0 Then
Dim fi As TPDFFontInfo=fFontsInfos.Get(calcFontKey(fContext.fFontFamily,fContext.fFontStyle)).As(TPDFFontInfo)
Dim ts As TPDFTextSize=gTextSize(atext)
End If
If Bit.And(fContext.fFontstyle,fontUnderline)<>0 Then
contentAdd(fPage,$"${NumberFormat2(ax*fUnitFactor,1,3,3,False)} ${NumberFormat2(ay*fUnitFactor+fi.fYUnderline*fContext.fFontSize/1000,1,3,3,False)} ${NumberFormat2(ts.fWidth*fUnitFactor,1,3,3,False)} ${NumberFormat2((fi.fHUnderline)*fContext.fFontSize/1000,1,3,3,False)} re f ${Chr(10)}"$)
End If
If Bit.And(fContext.fFontstyle,fontStrikeThrough)<>0 Then
contentAdd(fPage,$"${NumberFormat2(ax*fUnitFactor,1,3,3,False)} ${NumberFormat2(ay*fUnitFactor+fi.fYStrikeThrough*fContext.fFontSize/1000,1,3,3,False)} ${NumberFormat2(ts.fWidth*fUnitFactor,1,3,3,False)} ${NumberFormat2((fi.fHStrikeThrough)*fContext.fFontSize/1000,1,3,3,False)} re f ${Chr(10)}"$)
End If
Return Me
End Sub
'draw text on current page at position ax,ay with current font and current text color
'explicit CRLF in atext
'automatic CRLF when awidth is reached
'(0,0) is bottom left of the page
'(w,h) if top right of the page
public Sub outTextFlow(aleftMargin As Double,arightMargin As Double,aalign As String,atext As String) As cPDF
Dim mts As TPDFMultilineTextSize=gMultilineTextSize(arightMargin-aleftMargin,atext)
Dim ts As TPDFTextSize
For p=0 To mts.fParagraphs.Size-1
Dim lines As List=mts.fParagraphs.Get(p).As(List)
For i=0 To lines.Size-1
Dim l As String=lines.Get(i)
ts=gTextSize(l)
Select Case aalign
Case "L"
outText(aleftMargin,fContext.fy,l)
Case "C"
outText(aleftMargin+(arightMargin-aleftMargin-ts.fWidth)/2,fContext.fy,l)
Case "R"
outText(arightMargin-ts.fWidth,fContext.fy,l)
Case "J"
If i=lines.Size-1 Then
'last line if left aligned
outText(aleftMargin,fContext.fy,l)
Else
'other lines are justified
Dim w() As String=Regex.split(" ",l)
Dim x As Double=aleftMargin
'calc space between each word
Dim s As Double=((arightMargin-aleftMargin)-ts.fWidth+(gTextSize(" ").fWidth*(w.Length-1)))/(w.Length-1)
For j=0 To w.Length-1
Dim ww As TPDFTextSize=gTextSize(w(j))
outText(x,fContext.fy,w(j))
x=x+s+ww.fWidth
Next
End If
End Select
fContext.fX=aleftMargin
fContext.fy=fContext.fy+ts.fHeight
Next
Next
Return Me
End Sub
'draw a line on current page from ax1,ay1 to ax2,ay2 with current width and color
'(0,0) is bottom left of the page
'(w,h) if top right of the page
public Sub outLine(ax1 As Double,ay1 As Double,ax2 As Double,ay2 As Double) As cPDF
contentAdd(fPage,$"${NumberFormat2(ax1*fUnitFactor,1,3,3,False)} ${NumberFormat2(ay1*fUnitFactor,1,3,3,False)} m ${NumberFormat2(ax2*fUnitFactor,1,3,3,False)} ${NumberFormat2(ay2*fUnitFactor,1,3,3,False)} l h S${Chr(10)}"$)
Return Me
End Sub
'draw a rectangle on current page starting at ax,ay with dimension aw,ah,current width, color and textAndFillColor are used
'(0,0) is bottom left of the page
'(w,h) if top right of the page
'astyle : use predefined constants RectangleBorderOnly,RectangleFillOnly,RectangleBorderAndFill
public Sub outRectangle(ax As Double,ay As Double,aw As Double,ah As Double,astyle As String) As cPDF
If astyle=RectangleBorderAndFill Or astyle=RectangleFillOnly Then
contentAdd(fPage,$"${NumberFormat2(fContext.fFillColor(0),1,3,3,False)} ${NumberFormat2(fContext.fFillColor(1),1,3,3,False)} ${NumberFormat2(fContext.fFillColor(2),1,3,3,False)} rg${Chr(10)}"$)
End If
contentAdd(fPage,$"${NumberFormat2(ax*fUnitFactor,1,3,3,False)} ${NumberFormat2(ay*fUnitFactor,1,3,3,False)} ${NumberFormat2(aw*fUnitFactor,1,3,3,False)} ${NumberFormat2(ah*fUnitFactor,1,3,3,False)} re ${astyle} ${Chr(10)}"$)
Return Me
End Sub
private Sub convertImage(adir As String,afile As String) As String
'convert to JPG
Dim img As B4XBitmap
img=fXUI.LoadBitmap(adir,afile)
Dim out As OutputStream=File.OpenOutput(adir,afile & ".jpg",False)
img.WriteToStream(out,100,"JPEG")
out.close
'then convert to PNG
Dim img As B4XBitmap
img=fXUI.LoadBitmap(adir,afile & ".jpg")
Dim out As OutputStream=File.OpenOutput(adir,afile & ".png",False)
img.WriteToStream(out,100,"PNG")
out.close
File.Delete(adir,afile & ".jpg")
Return afile & ".png"
End Sub
'draw an image
'only PNG
public Sub outImage(adir As String,afile As String,ax As Double,ay As Double,aw As Double,ah As Double) As cPDF
Dim ii As TPDFImageInfo
afile=convertImage(adir,afile)
'check if this image is already used
Dim k As String=imageKey(adir,afile)
Dim i As Int=findImageKey(k)
'if not add it to the list
If i=-1 Then
'get informations about the image
ii=imageInfo(adir,afile)
'store it in the list
fUsedImages.add(CreateMap("path":k,"info":ii))
i=fUsedImages.Size-1
Else
ii=fUsedImages.Get(i).As(Map).Get("info")
End If
File.Delete(adir,afile)
If ii.fError=0 Then
'originals width and height
If aw=0 And ah=0 Then
aw=-96
ah=-96
End If
'-aw is horizontal resolution in dpi
If aw<0 Then
aw=-ii.fWidth*72/aw/fUnitFactor
End If
'-ah is vertical resolution in dpi
If ah<0 Then
ah=-ii.fHeight*72/ah/fUnitFactor
End If
'calc aw proportionnaly to ah
If aw=0 Then
aw=ah*ii.fWidth/ii.fHeight
End If
'calc ah proportionnaly to aw
If ah=0 Then
ah=aw*ii.fHeight/ii.fWidth
End If
contentAdd(fPage,$"q ${NumberFormat2(aw*fUnitFactor,1,3,3,False)} 0 0 ${NumberFormat2(ah*fUnitFactor,1,3,3,False)} ${NumberFormat2(ax*fUnitFactor,1,3,3,False)} ${NumberFormat2(ay*fUnitFactor,1,3,3,False)} cm /I${i} Do Q${Chr(10)}"$)
End If
Return Me
End Sub
'add raw content to the current page
'all the positions and dimensions are in user unit
'LF is added at the end of text
public Sub outRaw(atext As String) As cPDF
If fUnitFactor<>1 Then
'convert user unit in atext to pt
Dim shift As Int=0
atext=atext & Chr(10)
Dim m As Matcher = Regex.Matcher("([+-]?[0-9]{1,}[\.]{0,1}[0-9]{0,})",atext)
Do While m.Find
For g=1 To m.GroupCount
Dim v As String=NumberFormat2(fUnitFactor*m.Group(g),1,3,3,False)
atext=atext.SubString2(0,m.getstart(g)+shift) & v & atext.SubString(m.GetEnd(g)+shift)
shift=shift+v.Length-m.Group(g).Length
Next
Loop
End If
contentAdd(fPage,atext)
Return Me
End Sub
'set a document's property
'use predefined constants propertyAuthor....
public Sub sProperty(aproperty As String,avalue As String) As cPDF
fProperties.Put(aproperty,avalue)
Return Me
End Sub
'convert signed byte to unsigned
private Sub ToUnsigned(b As Byte) As Int
Return Bit.And(0xFF, b)
End Sub
'get the text size with the current font and size
'fwidth : width of the text
'fTop : distance between baseline and top of the text
'fBottom : distance between baseline and bottom of the text
'fHeight : total height of the text
public Sub gTextSize(atext As String) As TPDFTextSize
Dim bb As B4XBytesBuilder
Dim ts As TPDFTextSize
bb.Initialize
bb.Append(atext.GetBytes(fTextEncoding))
ts.Initialize
Dim fi As TPDFFontInfo=fFontsInfos.Get(calcFontKey(fContext.fFontFamily,fContext.fFontStyle)).As(TPDFFontInfo)
Dim w As Double
For i=0 To bb.Length-1
w=w+fi.fCharsWidths(ToUnsigned(bb.InternalBuffer(i)))
Next
ts.fWidth=w*fContext.fFontSize/1000/fUnitFactor
ts.fTop=fi.fYTop*fContext.fFontSize/1000/fUnitFactor
ts.fBottom=fi.fYBottom*fContext.fFontSize/1000/fUnitFactor
ts.fHeight=ts.fBottom-ts.fTop
Return ts
End Sub
'add line to TPDFMultilineTextSize and return rest of line
private Sub MultilineTextSizeAddLine(amts As TPDFMultilineTextSize,ats As TPDFTextSize,atext As String,awidth As Double) As String
Dim s As String
If ats.fWidth>awidth Then
Dim p As Int=atext.LastIndexOf(" ")
If p=-1 Then
p=atext.Length-1
End If
s=atext.SubString(p+1)
atext=atext.SubString2(0,p)
Else
s=""
End If
amts.fparagraphs.Get(amts.fParagraphs.Size-1).As(List).Add(atext)
amts.fHeight=amts.fHeight+ats.fHeight
Return s
End Sub
'get the size of multiline text with the current font and size
public Sub gMultilineTextSize(awidth As Double,atext As String) As TPDFMultilineTextSize
Dim mts As TPDFMultilineTextSize
Dim ts As TPDFTextSize
Dim c As Char
Dim l As String=""
mts.Initialize
mts.fParagraphs.Initialize
mts.fWidth=awidth
'split text with CRLF
Dim paragraphs() As String=Regex.Split(CRLF,atext)
Dim p As String
For j=0 To paragraphs.Length-1
Dim lines As List
lines.Initialize
mts.fParagraphs.Add(lines)
p=paragraphs(j)
l=""
For i=0 To p.Length-1
c=p.CharAt(i)
If Asc(c)>31 Then
l=l & c
ts=gTextSize(l)
If ts.fWidth>awidth Then
l=MultilineTextSizeAddLine(mts,ts,l,awidth)
End If
End If
Next
ts=gTextSize(l)
l=MultilineTextSizeAddLine(mts,ts,l,awidth)
Next
Return mts
End Sub
public Sub sX(avalue As Double) As cPDF
fContext.fX=avalue
Return Me
End Sub
public Sub sY(avalue As Double) As cPDF
fContext.fy=avalue
Return Me
End Sub
public Sub gX As Double
Return fContext.fX
End Sub
public Sub gy As Double
Return fContext.fy
End Sub
'get the lib version
public Sub glibversion As String
Return fLibVersion
End Sub