PDFGraphics DrawPicture

Does anyone know a way to get the PDF Graphics to NOT draw pictures scaled down in resolution?
I had hoped drawing a larger image to a reduced size would address the resolution, but the output is not printable and therefore not usable.

Can you share your code and the image width, height and resolution values ?

Var w,h As Double
Var s As Double

w = inchToPixel(2)
h = inchToPixel(0.5)

Var pic As picture = ReportHeaderGraphic
s = Min(w/pic.Width, h/pic.Height)

Var pw, ph As Double
pw = pic.Widths
ph = pic.Height
s

g.DrawPicture pic, margins, margins, pw, ph, 0,0, pic.Width, pic.Height

The image is 900pixels wide by 200 tall.

Using DynaPDF this looks great, but there are some features of DynaPDF (using itā€™s Xojo graphics methods) that donā€™t seem to be implemented yet, so Iā€™m really hoping to use the native Xojo classes.

If you miss something, I may be able to add more methods.
But if you use DynaPDFā€™s native API, you may already have more capabilities. And you can mix both ways.

Christian, it seems the GraphicsPath isnā€™t implemented (unless Iā€™m making a mistake).
I had thought about using DynaPDF native, but at this point I have a ton of graphics that are drawn to canvases and such that I can simply redirect to the PDFDocument. The GraphicsPath stuff is pretty significant I this solution, so it would be hard to rework. ** should I be seeing GraphicsPath working?

GraphicsPath is implemented, but a bit different than you expect.

Since you canā€™t inspect a GraphicsPath object, we had to subclass it, overwrite all methods and record what you draw. That is why we have a DynapdfGraphicsPathMBS class. You can use with normal graphics class, too.
Here some sample code:

Sub draw(g as graphics, pdf as DynaPDFMBS = nil)
			Const Pi = 3.14159
			Dim pi2 As Double = pi * 2.0
			
			Var gp As DynapdfGraphicsPathMBS
			g.DrawingColor = &cFF0000
			g.PenSize = 5
			g.PenHeight = 5
			g.PenWidth = 5
			
			Title = Str(g.PenWidth)+"x"+Str(g.PenHeight)
			'g.DrawLine 0, 0, 100, 100
			g.PenSize = 1
			
			// arc
			g.AntiAlias = True
			g.PenSize = 3
			
			Dim r As Integer = 40
			Dim n As Integer = 0
			For w As Integer = 0 To 350 Step 10
				
				Select Case n Mod 4
				Case 0
					g.DrawingColor = &cFF0000
				Case 1
					g.DrawingColor = &c00FF00
				Case 2
					g.DrawingColor = &c0000FF
				Case 3
					g.DrawingColor = &cFF00FF
				End Select
				n = n + 1
				
				Dim w0 As Double = w
				
				Dim w1 As Double =  w0       / 180.0 * pi
				Dim w2 As Double = (w0+10.0) / 180.0 * pi
				Dim clockwise As Boolean = False
				
				If pdf <> Nil Then
					'w1 = Convrt(w1)
					'w2 = Convrt(w2)
					
					'w1 = pi2 - w1
					'w2 = pi2 - w2
					'clockwise = True
				End If
				
				gp = New DynapdfGraphicsPathMBS
				gp.AddArc(200, 200, r, w1, w2, clockwise)
				g.DrawPath gp
				
				r = r + 1
				
			Next
			
			
			g.DrawingColor = &c00FFFF
			
			
			Dim w1 As Double = 0
			Dim w2 As Double = pi/2
			Dim clockwise As Boolean = False
			
			gp = New DynapdfGraphicsPathMBS
			gp.AddArc(200, 200, 80, w1, w2, clockwise)
			g.DrawPath gp
			
			
			Var curve As New DynapdfGraphicsPathMBS
			curve.MoveToPoint(320, 200+20)
			curve.AddCurveToPoint(320, 200+100, 500, 200+100, 500, 200+20)
			g.DrawingColor = &c00FF00
			g.DrawPath(curve)
			
			// Draw a fluffy white cloud
			Var cloud As New DynapdfGraphicsPathMBS
			cloud.MoveToPoint(270, 80)
			cloud.AddCurveToPoint(230, 100, 230, 150, 330, 150)
			cloud.AddCurveToPoint(350, 180, 420, 180, 440, 150)
			cloud.AddCurveToPoint(520, 150, 520, 120, 490, 100)
			cloud.AddCurveToPoint(530,  40, 470,  30, 440,  50)
			cloud.AddCurveToPoint(420,   5, 350,  20, 350,  50)
			cloud.AddCurveToPoint(300,   5, 250,  20, 270,  80)
			
			g.DrawingColor = &c0000FF
			g.PenSize = 5
			g.DrawPath(cloud)
			
			
			Var p As New DynapdfGraphicsPathMBS
			p.MoveToPoint(10, 5) // Start location
			p.AddLineToPoint(40, 40)
			p.AddLineToPoint(5, 60)
			
			g.DrawingColor = &c0000FF
			g.DrawPath(p, True)
			
			
			Var qCurve As New DynapdfGraphicsPathMBS
			qCurve.MoveToPoint(38, 100)
			qCurve.AddQuadraticCurveToPoint(138, 0, 238, 100)
			
			g.DrawingColor = &c00FF00
			g.PenSize = 10
			g.DrawPath(qCurve)
			
			Var rect As New DynapdfGraphicsPathMBS
			rect.AddRectangle(300+10, 300+10, 100, 150)
			g.DrawingColor = &cFF0000
			g.DrawPath(rect)
			
			rect = New DynapdfGraphicsPathMBS
			rect.AddRoundRectangle(10, 300+10, 100, 150, 10, 10)
			g.DrawPath(rect)
		End Sub
1 Like

And this is why I renew your plugins every single year.

Thanks, Christian. When Covid is over you are getting a hug! (not really).

1 Like

When Covid is done, weā€™ll do some traveling for sure!
I may visit a few friends and do a few local Xojo meetings.

1 Like

What resolution ?

You do not use:
Picture.HorizontalResolution

Picture.VerticalResolution

http://documentation.xojo.com/api/graphics/picture.html#picture-horizontalresolution

So, if your image ( 900 x 200 ) is 72 dpi, you always get this result.

If the image reslution is - say - 300 dpi or more, set the above properties to that number.

HTH

1 Like

really, it should be using total pixels regardless of resolution. Resolution only dictates display width/height, and Iā€™m overriding these in the drawing command.

Regardless, Iā€™ve seamlessly switched to DynaPDF and it will be good.

Thanks, Emile.

3 Likes

Resolution only dictates display width/height
No. It means print quality.
Nota: it would take you less time to make te-he change and test (if th compile is fast) than writing an answer.

Tink differently: why Retina / HDPi (dmonitor display) are no more 72 / 96 dpi but far more ?

In Photoshop (but not only), you can choose to display all pixels or display the image as is: yes, in that case it means a difference in size. But at print time, you get the same size / different quality.

HTH

Thanks, Emile. I got this covered.
MBS plugins covers the issue exactly by fitting the width and height of the original into the destination without discarding pixels.

1 Like

Please remember to call SetResolution like this:

call pdf.SetResolution(300)

with the resolution you want to have as maximum.
DynaPDF will check what size the image is shown and limit resolution to the setting you pass. Default is 150 dpi. This avoids excessive image sizes.

1 Like

Good note.
150 is fine for this purpose.
Thanks, Christian.

Isnā€™t it what I said earlier ?

Emile,
I believe itā€™s exactly what you said. It just wasnā€™t working for me, and I think I know why. However there are so many other benefits to the MBS plugin that it makes it a moot point to chase it right now.

Here is my more detailed explanation:

If I do the following:

Var p As New PDFDocument
Var pic As picture= MyLogo

Var newWidth As Double = 150 
Var theScale As Double = newWidth/pic.Width
Var newHeight As Double = pic.Height*theScale

pic.HorizontalResolution = 600
pic.VerticalResolution= 600

p.Graphics.DrawPicture pic, 0,0, newWidth, newHeight, 0,0, pic.Width, pic.Height

Var f As FolderItem = SpecialFolder.Desktop.child("test xojo.pdf")
If f.Exists Then f.Delete

p.save(f)
f.Open(True)

I know that changing the resolution doesnā€™t add more pixels.
I get a low resolution output. Looks to be 72dpi. Jaggies. Yuck.
I tried this after your first suggestion.

I know you also know this too, but others may read the thread and I want to offer a clear message to anyone with less graphics understanding:

A picture has a set amount of information (pixels) those pixels can be presented at any resolution you want, but the size of the image is changedā€¦ An image that is 300 dpi and is 1 inch wide can be scaled to 1/2 inch at 600dpiā€¦ no data loss. Of course higher resolution doesnā€™t always increase quality - there is a point of diminishing returns (my customer just sent me a 1 page US letter sized PDF with such large images that the PDF, compressed, was 750mbā€¦ for screen presentation)

My expectation of the PDFGraphics is that setting an image in a given size in the PDF document should retain the total pixel count as PDF are resolution independent. That isnā€™t whatā€™s happening. I thought it may be like a printer where you can set a higher resolution for the output drawing, but I canā€™t see how to do that. I believe this is just a limitation of the newly added PDFDocument capability. And Iā€™m not knocking the PDFDocument additionsā€¦ I think they are great.

On the other hand, when I use Christianā€™s plugin like this (below), I get exactly what I expect.

Var pdf As New DynaPDFMBS
Var f As FolderItem = SpecialFolder.Desktop.child(ā€œtest dyna.pdfā€)
If f.Exists Then f.Delete
Call pdf.CreateNewPDF(f)

Call pdf.Append
Var g As graphics
g = pdf.PageGraphics

Var pic As picture= MyLogo

Var newWidth As Double = 150
Var theScale As Double = newWidth/pic.Width
Var newHeight As Double = pic.Height*theScale

g.DrawPicture pic, 0,0, newWidth, newHeight, 0,0, pic.Width, pic.Height
Call pdf.CloseFile
f.Open(True)

I recognize that there is a way to set the resolution on DynaPDF, but the default 150 is perfect for my current purpose.

Considering that I own a license for the copy of DynaPDF, that itā€™s no more code to write, and that I can extend what Iā€™m doing to include XMP, and spot color elements, itā€™s kind of a no brainer for me at this time.

So yes, I think Iā€™m agreeing with your line of reasoning.

Orā€¦ I have been up since 1am last night, so I could be missing your point completely! (惄)

1 Like

Var newWidth As Double = 150
This is the image Width, not Resolution.

the default 150 is perfect for my current purpose.
My Retina screen dpi isā€¦ 144 (current setting), but it can be more (default setting).

Note to self: I am doing so many errors ! Otherā€™s errors are so easy to find, why mine are so hard (for me) ? :wink:

Things change with timeā€¦

This was an example to test being able to add an image at a higher resolution, not my actual code.
Iā€™m aware 159 is the width of the placement.
The resolution should have been 600dpi in the pdf document version and 159 in the DynaPDF version.

Thanks, Emile.

I know that this is an old thread, but I wanted to reply because this is an issue that I have come across and I seem to have figured out a workaround that just uses the native xojo PDFGraphics class.

Instead of scaling the picture like this (which results in poor resolution):

g.DrawPicture (Image, x, y,destWidth, destHeight, sourceX, sourceY, sourceWidth, sourceHeight)

I have discovered that if you use Graphics.Scale instead, then the resolution is good :slight_smile:

I have tested this with the following code adapted from one of the Xojo blog posts on PDF rotation and scaling:

Var d As New PDFDocument
Var g As Graphics = d.Graphics
Var p As Picture = Picture.Open(SpecialFolder.Desktop.Child("r2d2.png"))

If p <> Nil Then
  g.DrawPicture(p, 0, 0, p.Width/2, p.Height/2, 0, 0, p.Width, p.Height)
  
  Var sf As Double = 0.5
  g.Scale(sf, sf)
  g.DrawPicture(p,0,p.Height)
  
  Var f As FolderItem = SpecialFolder.Desktop.Child("RotateScaleTranslate.pdf")
  d.Save(f)
  f.Open
  
End If

You will see that it will draw the same picture twice, both the same size, but the first one has poor resolution while the second one looks crisp

I hope this helps someone else as it was a frustrating problem to troubleshoot!

Cheers,
Frank

That is great.
Thanks for sharing.