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 <> 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 <> 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 <> 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>${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)}<>${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)}<>${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