PDF Class

For those of you interested… I am nearing completion of a new PDF class for XOJO. This one is a bit different from all the others out there. It is not designed to utilize all the functions and features of PDF, but more to emulate the GRAPHICS object that exists in XOJO today. I believe that this will cover alot of the needs developers have for PDF, without adding the overhead and complexity required by other commercial PDF classes.

If has all the properties of a GRAPHICS object, except for AntiAlias, Copies, Firstpage,Handle, Lastpage,PrintingCanceled,TextUnit, Transparency and UseOldRenderer
For the most part because most of these properties are specific to the direct use of a PRINTER, or in creating a PICTURE object.

It adds the following properties that are for direct support of PDF. Author, Subject, Title, Landscape, Style (dotted/dashed lines).

If supports all the methods of a GRAPHICS object, expect for CLIP, DrawCautionIcon, DrawNoteIcon,DrawStopIcon, Pixel and StringDirection
It adds the following new methods… Margins and SavePDF

The first version to be released will only support the PDF Base14 fonts which are Normal/Bold/Italic versions of Courier, Arial/Helvetica, Times, Symbol and Dingbats. Future version will hopefully support all fonts types.

Example of use

pdf=new SimplePDF
pdf.forecolor=&cff0000
pdf.textfont="Courier"
pdf.textsize=24
pdf.drawstring "Hello World",100,100
pdf.author="Dave"
pdf.subject="This is a test"
pdf.SavePDF(f) ' where F is a folderitem

I am posting this now for two reasons… One to see how much interest there is in a light-weight PDF class… and to see if anyone has any ideas for features without straying from the GRAPHICS OBJECT paradigm.

Good progress, it seems, Dave! You mention that it supports all methods of Graphics except those named. Have you actually implemented the drawObject method? If so, you are really fast! (Either that or really sleepy about now) If not, I think it would be a great future addition.

I knew I forgot something… DRAWOBJECT is NOT implemented… and if it is will be the last one… I need to figure out a way to deconstruct a OBJECT2D into individual FIGURESHAPE, CURVESHAPE etc… so they can be drawn one at a time… the only other way is to CHEAT and draw it into an offsite PICTURE and the use DRAWPICTURE (which also is not done,… but in progress)

Dave here is some code to help you out with the Figureshape:

[code] Dim k as integer

'c.LineTo(Shape.item(0).X,c.convertToPDFaxis(Shape.item(0).Y,0))
if shape.Fill = 0 and shape.Border = 100 then
c.SetLineWidth shape.BorderWidth
c.ConvertToPDFColor(shape.borderColor, “Stroke”)
for k =0 to shape.Count-1
vCurveShape(c,Shape.item(k))
next
c.closePath
c.StrokePath
elseif shape.Fill = 100 and shape.Border = 100 then
c.SetLineWidth shape.BorderWidth
c.ConvertToPDFColor(shape.FillColor, “Fill”)
c.ConvertToPDFColor(shape.borderColor, “Stroke”)
for k =0 to shape.Count-1
vCurveShape(c,Shape.item(k))
next
c.closePath
c.CloseFillStrokePath
elseif shape.Fill = 100 and shape.Border = 0 then
c.SetLineWidth shape.BorderWidth
c.ConvertToPDFColor(shape.borderColor, “Fill”)
for k =0 to shape.Count-1
vCurveShape(c,Shape.item(k))
next
c.closePath
c.FillPath
elseif shape.Fill < 100 and shape.Fill > 0 and shape.border = 0 then

elseif shape.Fill < 100 and shape.Fill > 0 and shape.border = 100 then
c.SetLineWidth shape.BorderWidth
c.ConvertToPDFColor(shape.FillColor, “Fill”)
c.ConvertToPDFColor(shape.borderColor, “Stroke”)
c.FillWithTransparency(shape.Fill)
for k =0 to shape.Count-1
vCurveShape(c,Shape.item(k))
next
c.closePath
c.CloseFillStrokePath
end if[/code]
vCurveShape[code]
If beginDot = true then
c.moveTo(Shape.X,c.convertToPDFaxis(Shape.Y,0))
beginDot=false
end if

Select case shape.Order

case 0
if CurveEnds = true then
c.SetLineWidth shape.BorderWidth
c.ConvertToPDFColor(shape.borderColor, “Stroke”)
c.LineTo(shape.X2,c.convertToPDFaxis(shape.Y2,0))
c.StrokePath
else
c.LineTo(shape.X2,c.convertToPDFaxis(shape.Y2,0))
end if
case 1
c.CurveToLeft(shape.ControlX(0),c.convertToPDFaxis(shape.ControlY(0),0), shape.X2,c.convertToPDFaxis(shape.Y2,0) )
case 2
c.CurveTo(shape.ControlX(0),c.convertToPDFaxis(shape.ControlY(0),0), shape.ControlX(1),c.convertToPDFaxis(shape.ControlY(1),0), shape.X2,c.convertToPDFaxis(shape.Y2,0))
end select
[/code]

The object primitives are not the issue… the issue is when they are appended into a GROUP2D object. I cannot see anyway to deconstruct that back into an array or collection of the primitives.

But for my current purposes… that is the lowest priority…

I have modify the DRAWSTRING to support WORDWRAP and CONDENSE
and add DRAWPICTURE

Does anyone have any xplat methods for the compression used in PDF? Right now I have plain text mode for the file format

Yes; of course, I didn’t think about that. Deconstructing Group2D, as well as FigureShape seems like it should be possible, but I don’t know how. Once an element is appended or added, there seems no way to get to it. Drawing Object2Ds as a picture is better than nothing, but it would sure be nice to be able to retain their vector nature.

I suppose you could create your own PDF-ready analogs to each of the object2D subclasses, but that would limit the ability of the user to use your PDF class as a near drop in replacement for the Graphics object…

Hmmm, maybe you could subclass each of the composite Object2D subclasses ( e.g., “myExtendedGroup2D” as a subclass of Group2D, “myExtendedFigureShape” as a subclass of FigureShape). You could store the component data in each of these subclasses for your use in PDF generation, but the same subclassed objects could also be used directly in Graphics.drawObject.

Now that I think of it, this might be handy in other cases as well; I’ve occasionally wished that I could “look into” a composite Object2Ds to see what’s within…

Well this is what I have for Object2D. Sorry to dump so much code into your conversation. I just felt the need to help out.

[code] Dim k, count as integer
Dim obj as object2D
Dim temp as string
obj = new object2D
count=shape.count

For k = 0 to count-1
'msgBox str(k)
prevousObj=false
obj=shape.item(k)
if obj isA ArcShape then
vArcShape(c,ArcShape(obj))
prevousObj=true
end if
if obj isA CurveShape then
beginDot = true
CurveEnds = true
vCurveShape(c,CurveShape(obj))
prevousObj=true
end if
if obj isA FigureShape then
beginDot = true
vFigureShape(c,FigureShape(obj))
prevousObj=true
end if
if obj isA Group2D then
vGroup2D(c,Group2D(obj))
prevousObj=true
end if
if obj isA OvalShape and prevousObj = false then
vOvalShape(c,OvalShape(obj))
prevousObj=true
end if
if obj isA RectShape and prevousObj = false then
vRectShape(c,RectShape(obj))
prevousObj=true
end if
if obj isA PixmapShape then
vPixmapShape(c,PixmapShape(obj))
end if
if obj isA StringShape then
vStringShape(c,StringShape(obj))
end if
next
[/code]

Why not use Group2D.Item along with Group2D.Count and the same for figureshape?

  • Karen

As far as I know, the most common compression used in PDFs is flate, and I don’t know of any Xojo mechanism that implements that. Based on the output you posted earlier in the week, you could make them somewhat smaller by getting rid of the unnecessary spaces and line breaks in the output.

I’d suggest, though, putting that at the bottom of your list. For the purposes you’ve described, I don’t think most of your output will be that large in file size. Plus the PDFs should be very highly compressible after they’re made if a user needs to email or store in a database, etc. Also, debugging your class will be enormously easier if the data are not internally compressed…

Oy! I always learn something from you, Karen! I’d never noticed the “Item” function! What an idiot…

Dave, you can probably use Thomas’s Zip classes at for the compression used in a PDF file.

Thomas’s Zip classes on forum.

Sean … no worries… this is a learning experience for everyone… so what can’t be used here may be usable somewhere else.

The ironic thing (to me)… is when I started working at General Dynamics (a long time ago)… I had a requirement to create PDF files from a programming language called SAS. (they now have this native, but didn’t back in the mid 80’s)… It took me 2 months to write a program similar to what I am working on now… and it could only do text in ONE font, and only draw straight lines. This Xojo version is pretty much feature complete (for my design spec) and I’ve got less than 3 days work into it :slight_smile:

Very impressed, Dave. Well done!

One reason I stopped using Asher’s classes and started with DynaPDF is that there is a method in a PDF file to re-use a picture in a number of places.
So if you can imagine a space invaders sprite, and you need 200 instances of it, you embed the sprite once, then display it many times.
It makes an enormous difference to the size of a PDF where a picture is repeated.

(My main problem with DynaPDF for a few years has been transparency: The Starter edition would have been enough, but the transparency feature pushes things into the Lite edition, which is massively more expensive. Im watching this space with a great deal of interest… ;> :slight_smile:

Well I have all the major stuff working… except

DRAWPICTURE… I have to implement some zip functionality and figure out the write format to put the data in first.
DRAWOBJECT… Need to look into what Karen said above
DRAWSTRING currently does not support CONDENSE (and seems Xojo doesnt support it consistently either :slight_smile: ) and only supports a few fonts (based on PDF BASE14 standards)

But I believe I have everything else working fairly well.

That’s great, Dave!

I think the font restriction is not too onerous at the outset. Figuring out how to embed arbitrary fonts is a pretty easily compartmentalized task, I would thing, and I’m guessing that most folks would rather live with the base fonts than wait for that to be implemented…

Embedding fonts in a general way is about what Asher started before he headed off to college and its a big job :stuck_out_tongue:
First you have to be able to read all the different font formats that are supported on each OS then be able to extract & embed the glyphs (or the entire font)

Well, that is true, but I think a first step might be to support just one cross-platform font type - e.g., OpenType (keeping in mind that OpenType has two flavors - one with PostScript Type1 outlines, and the other with TrueType outlines.

You raise a good point also, Norman, in mentioning embedding selected glyphs (font subsetting) as embedding the entire font for each font used in the document can make the PDF file size grow rather quickly. Figuring out subletting, though, complicates the task further…

Holding off on addtional fonts for the moment… .working on DRAWPICTURE and not having a good time…

I take the passed picture object in the DRAWSTRING command…
and execute this code to make a PDF Xobject…
Notice I have tried both DCTDecode and ASCIIHexDecode

but no matter what … this causes a corrupt PDF file… and the XREF table seems fine…

  Dim w As Integer
  Dim h As Integer
  Dim s As String
  dim i as integer
  w=p.width
  h=p.height
  Dim mb As MemoryBlock
  mb=p.GetData(p.FormatJPEG,p.QualityMax)
  //
  PDF_WRITE "<< /Type /XObject /SubType /Image"
  PDF_WRITE "/Width "+Str(w)+" /Height "+Str(h)+" /Length "+Str(mb.size)
  PDF_WRITE "/ColorSpace /DeviceRGB /BitsPerComponent 8 /Filter"
  //PDF_WRITE "/DCTDecode"
  pdf_write "ASCIIHexDecode"
  PDF_WRITE "stream"
  for i=0 to mb.size-1
    //PDF_TEXTSTREAM.Write chrb(mb.Byte(i))
    PDF_TEXTSTREAM.Write right("00"+hex(mb.Byte(i)),2)+" "
  next i
  PDF_TEXTSTREAM.Write EndOfLine.UNIX
  PDF_WRITE "endstream"
  PDF_WRITE "endobj"

I would ignore embedding fonts at this point. If someone really needs the font embedded it is easy to call gs from the command line to embed all fonts used. I do this for PDFs I generate in a Picklike system.

I may easily be missing something, but why are you adding the space at the end of:

    PDF_TEXTSTREAM.Write right("00"+hex(mb.Byte(i)),2)+" "

?