-
Notifications
You must be signed in to change notification settings - Fork 795
TransformGroup and the graphics state stack #234
Comments
Wow -- good observation. This catches me entirely by surprise. You are correct about saving and resetting attributes as the following example shows: package main
import (
"fmt"
"os"
"github.com/jung-kurt/gofpdf/v2"
)
func main() {
pdf := gofpdf.New("P", "mm", "A4", "")
st := gofpdf.StateGet(pdf)
pdf.AddPage()
pdf.TransformBegin()
pdf.TransformTranslateX(0)
pdf.SetFillColor(255, 127, 0)
pdf.Rect(0, 0, 10, 10, "F")
pdf.TransformEnd()
st.Put(pdf)
pdf.TransformBegin()
pdf.TransformTranslateX(10)
pdf.SetFillColor(255, 127, 0)
pdf.Rect(0, 0, 10, 10, "F")
pdf.TransformEnd()
fileStr := "transform.pdf"
err := pdf.OutputFileAndClose(fileStr)
if err == nil {
fmt.Printf("successfully generated %s\n", fileStr)
} else {
fmt.Fprintf(os.Stderr, "error %s: %s\n", fileStr, err)
}
} I will look into this more and see what I can uncover. I am confused by section 8.4 in the reference: it seems that it is the reader's responsibility to stack and unstack the attributes before and after a transformation. How do you read that? |
It seems like that's the case. In terms of fixing it I can see two solutions, the first being to remove the existing 'is this value already set' checks before many of the setters ( That could work like this:
We could also add public methods to manually save/restore the graphics state, like pdfkit is doing here None of these changes will affect the packages external API, but they should allow the internal state of gofpdf to be more accurate to the internal state of (properly conforming) readers. These changes could cause PDFs to render differently after implementing, but those changes should only be more accurate to the programmer's original intention. (Setting a fill color will, indeed, change the fill color) |
Thanks for the excellent summary, @joewestcott. My inclination is to implement your first solution. These 'set only if the values are different' checks are fewer than the number of possible graphic attributes that would need to be bundled into a stackable GraphicsState. (Table 52 in the specification shows a lot of parameters.) Regarding your second solution, when developing the charting functionality, I implemented the StateType structure so that the drawing of grids would not interfere with the current graphic state. The example shown above works because the fill color (among other graphic attributes) is being reset after the first transformation. This lets the fill color assignment work in the second transformation just as it did in the first. So a quick solution, slightly different than your solution, would be to simply push the return value of I'll mull this over a little more, but my instinct favors the first approach. |
I just commited 6b7c17b that makes graphic property assignments unconditional. ExampleFpdf_SetFillColor shows transformations working as expected. |
Amazing! Thanks for doing this. I've noticed a few more setters in the library that also exhibit the same issue, so I've submitted a PR (#235) removing state checks from the following methods. I think this is all of them! |
Good catches, @joewestcott -- I completely missed those. And thanks for testing the changes. |
I just factored the pattern drawing code in the transformation / graphic state test and noticed a bug. When I'll reopen this issue while I look into this some more. |
The following code should produce two orange rectangles at x:0 and x:10.
However, the second rectangle is black. It seems the second 'set' command is not writing the color change to the PDF, as gofpdf's internal state already has this color set. However, the call to
TransformBegin
has created a new graphics state stack in the PDF which has reset many font, line, and color parameters.To match the PDF spec It seems like we need to store our attributes in a stack, to be able to push them in
pdf.TransformBegin
, and pop the previous values back inpdf.TransformEnd
PDF spec, see 8.4 "Graphics State"
The text was updated successfully, but these errors were encountered: