Skip to content
This repository has been archived by the owner on Nov 13, 2021. It is now read-only.

Render problems with the pdf.Arc() function #282

Open
maberer opened this issue Jul 28, 2019 · 9 comments
Open

Render problems with the pdf.Arc() function #282

maberer opened this issue Jul 28, 2019 · 9 comments
Labels

Comments

@maberer
Copy link

maberer commented Jul 28, 2019

I need to create some very fine details (in the mm range) on certain parts of my PDF based on the pdf.Arc() function.

I found that if I create circle segments with the Arc() function using two different rx, ry settings, the endings of the segments do not align.

To illustrate the effect, I created the demo program (please see below), zoomed in with my PDF-viewer, and exported the image as a png. A few of the bad parts are encircled by a red circle. Some segments do align, like the one thats encircled with a green circle.

Because gofpdf creates the PDF as vector graphics, I expected the arc endings to align - independent of the size of the element.

I think that the effects seen within the red circles should not be there...

func testRender() {

	pdf := gofpdf.New("P", "mm", "A3", "")
	pdf.AddPage()

	const x = 200.0
	const y = 200.0
	const gapAngle = 40.00
	const segmAngle = 16.00
	const lineWidthOuterRing = 0.20
	const lineWidthInnerRing = 0.25

	drawArcs := func(val int, rx, ry float64) {

		for i := 19; i >= 0; i-- {
			drawSegm := (val & (0x01 << uint(i))) != 0
			if drawSegm {
				startAngle := segmAngle * float64(19-i)
				endAngle := startAngle + segmAngle
				rotAngle := 90.0 + (gapAngle / 2.0)
				pdf.Arc(x, y, rx, ry, rotAngle, startAngle, endAngle, "D")
			}
		}
	}

	pdf.SetFillColor(0, 0, 0)

	pdf.SetLineWidth(lineWidthOuterRing)
	drawArcs(0xFFAAAAB, 1.0, 1.0)

	pdf.SetLineWidth(lineWidthInnerRing)
	drawArcs(0xFFAAAAB, 0.775, 0.775)

	pdf.OutputFileAndClose("arcs.pdf")
}

pdf_render

@jung-kurt
Copy link
Owner

These results surprise me too. I'll look into this.

@jung-kurt
Copy link
Owner

I think the angle values used in the Arc() method are particularly sensitive to rounding errors. The misalignment artifacts are apparent even when the radius and line width values are increased by a factor of 100. Floating point values in a PDF are stored as text with a fixed precision. In gofpdf's case, the precision is five digits. It may be worth bumping this up further.

As an expedient in your particular example, eliminating one of the calls to DrawArcs() and using a line width of 0.45 seems to achieve the intended result.

@maberer
Copy link
Author

maberer commented Jul 28, 2019

Thanks for looking into this.

The example is primary made to illustrate the alignment problem. In my actual use case, I am using the first parameter of drawArcs() as a bitmask that can be different for the inner and outer circles - thus drawing only one circle would hide the problem but is not applicable in this case... Under normal conditions, the bit masks are not identical, but some segments will sometimes align and cause this undesired effect.

I would be grateful if we could bump up the resolution because the parts have to be machine readable thus require high precision...

@jung-kurt
Copy link
Owner

I bumped the precision of all floats stored by gofpdf from 5 to 9 and there was no discernible difference with the arc misalignment that you demonstrate. Since everything is smooth when one line width is used, I am inclined to think that the misalignment derives from a slight influence of line width in the way arc endpoints are calculated. I'll think about how this could be verified and, if it can be shown to be a problem, how it can be corrected.

@jung-kurt jung-kurt added the bug label Aug 12, 2019
@maberer
Copy link
Author

maberer commented Aug 29, 2019

Hi, is there a way to support you with this issue...? - I do not know the inner workings of the library but if there is something I can help you with - I do what I can...

@jung-kurt
Copy link
Owner

Thanks for the offer, @tindli. I haven't been able to figure out the source of this problem. One thing I have not investigated is whether the aberrations in the generated output are due to way a PDF renderer calculates arc angles with lines of varying thickness. Depending on the rendering engine's algorithm, the end point of an arc might only be approximately radial. (That is, the outside of the line might end at a different angle than the inside of the line.) In this case, the ending position would depend on where within the line the stop angle is applied.

One approach would be to write the absolute smallest PDF with gofpdf that demonstrates the problem. See if you can come up with a much simpler example with just two arcs of different thickness and no loops. This generated PDF could then be compared with the documents generated by other PDF packages.

@jung-kurt
Copy link
Owner

@tindli, once you write a simplified demonstration of the problem, you could see if documents made with jsPDF (which supports arcs) have or do not have the alignment problem that you found.

@maberer
Copy link
Author

maberer commented Sep 7, 2019

@jung-kurt Sorry for the late response. Was quite busy...

Now some of my results:

  1. I tried to reduce the script to a few simple statements without utilizing a for(). So I started to draw arcs with a simple start an end angle value. I tried several different angles but the arcs always lined up perfectly - thus I was not able to reproduce the issue with a simpler script.

  2. I tried more or less the same script like above with jsPDF. To my surprise, the error seems to exist there as well. See the code
    Result in the picture below:
    jspdf_precision_error

  3. I was able to avoid the issue by using a constant start/end angle values and only utilizing the rotation feature of gofpdf. Code
    So far, the results with this version have been far better.

I am not sure why this happens, and I am not sure why the function produces different results (using start+end+rotation vs. rotation only). Because jsPDF produces similar results, the problem might not be within gofpdf...

For my use case, I can probably go with the method described on step 3 (although I have to verify that further)...

Nevertheless, I am not really sure what to do with the issue...

@jung-kurt
Copy link
Owner

Thanks for the report, @tindli. These are really interesting findings.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

No branches or pull requests

2 participants