draw2d icon indicating copy to clipboard operation
draw2d copied to clipboard

Flipping the Y axis doesn't work with "draw2dpdf.GraphicContext"

Open koraye80 opened this issue 7 years ago • 4 comments

I've written an helper function to flip the image around the Y axis as in below:

// Flips the image around the Y axis. func invertY(gc draw2d.GraphicContext, height int) { gc.Translate(0, float64(height)) gc.Scale(1, -1) }

It works just fine with the "draw2dimg.GraphicContext", but silently fails if the current graphic context is in "draw2dpdf.GraphicContext". The "gofpdf" package seems to provide image transformation functions such as:

func (f *Fpdf) Transform(tm TransformMatrix) func (f *Fpdf) TransformBegin() func (f *Fpdf) TransformEnd() func (f *Fpdf) TransformMirrorHorizontal(x float64) func (f *Fpdf) TransformMirrorLine(angle, x, y float64) func (f *Fpdf) TransformMirrorPoint(x, y float64) func (f *Fpdf) TransformMirrorVertical(y float64) func (f *Fpdf) TransformRotate(angle, x, y float64) func (f *Fpdf) TransformScale(scaleWd, scaleHt, x, y float64) func (f *Fpdf) TransformScaleX(scaleWd, x, y float64) func (f *Fpdf) TransformScaleXY(s, x, y float64) func (f *Fpdf) TransformScaleY(scaleHt, x, y float64) func (f *Fpdf) TransformSkew(angleX, angleY, x, y float64) func (f *Fpdf) TransformSkewX(angleX, x, y float64) func (f *Fpdf) TransformSkewY(angleY, x, y float64) func (f *Fpdf) TransformTranslate(tx, ty float64) func (f *Fpdf) TransformTranslateX(tx float64) func (f *Fpdf) TransformTranslateY(ty float64)

So, is it possible to add some basic functionality to "draw2dpdf.GraphicContext", so that we can make use of these image transformation capabilities ? Thanks.

koraye80 avatar Jan 08 '18 09:01 koraye80

Hi @korayeyinc , Can you provide a code that reproduces the problem please? @stanim what do you think about this bug?

draw2dpdf already uses TransformTranslate and TransformScale in implementation of gc.Translate and gc.Scale. regards

llgcode avatar Jan 08 '18 13:01 llgcode

Hi @llgcode ,

Sure, the code below builds successfully using go version "go1.9.2" on a "linux/amd64" box -- i'm sorry about the formatting though, the code tag seems to unindent the code:

package main

import ( "image" "image/color"

"github.com/llgcode/draw2d" "github.com/llgcode/draw2d/draw2dimg" "github.com/llgcode/draw2d/draw2dpdf" )

var black = color.RGBA{0, 0, 0, 255}

// Flips the image around the Y axis. func invertY(gc draw2d.GraphicContext, height int) { gc.Translate(0, float64(height)) gc.Scale(1, -1) }

func check(err error) { if err != nil { panic(err) } }

// Generates PDF output. func genPDF() { height := 600 dst := draw2dpdf.NewPdf("L", "mm", "A4") gc := draw2dpdf.NewGraphicContext(dst)

gc.SetStrokeColor(black) gc.SetLineWidth(2)

gc.MoveTo(10, 10) gc.LineTo(100, 100) gc.QuadCurveTo(100, 10, 10, 10) gc.Close()

invertY(gc, height) gc.Stroke()

filename := "sample.pdf" err := draw2dpdf.SaveToPdfFile(filename, dst) check(err) }

// Generates PNG output. func genPNG() { width, height := 800, 600 dst := image.NewRGBA(image.Rect(0, 0, width, height)) gc := draw2dimg.NewGraphicContext(dst)

gc.SetStrokeColor(black) gc.SetLineWidth(2)

gc.MoveTo(10, 10) gc.LineTo(100, 100) gc.QuadCurveTo(100, 10, 10, 10) gc.Close()

invertY(gc, height) gc.Stroke()

filename := "sample.png" err := draw2dimg.SaveToPngFile(filename, dst) check(err) }

func main() { genPNG() genPDF() }

By the way, it generates PNG output successfully but throws the error "panic: transformation context is not active" during PDF generation. Thanks.

koraye80 avatar Jan 08 '18 14:01 koraye80

Thansk @korayeyinc , I think the problem is drawimg apply the transformation also on the path that is not already stroke or fill unlike drawpdf. Apply the transformation before creating the path (before gc.MoveTo), transformation have to be set before creating a path.

Just for tips: You can use the code blocks for code. I used the playground for guickly formatting.

package main

import (
	"image"
	"image/color"

	"github.com/llgcode/draw2d"
	"github.com/llgcode/draw2d/draw2dimg"
	"github.com/llgcode/draw2d/draw2dpdf"
)

var black = color.RGBA{0, 0, 0, 255}

// Flips the image around the Y axis.
func invertY(gc draw2d.GraphicContext, height int) {
	gc.Translate(0, float64(height))
	gc.Scale(1, -1)
}

func check(err error) {
	if err != nil {
		panic(err)
	}
}

// Generates PDF output.
func genPDF() {
	height := 600
	dst := draw2dpdf.NewPdf("L", "mm", "A4")
	gc := draw2dpdf.NewGraphicContext(dst)

	gc.SetStrokeColor(black)
	gc.SetLineWidth(2)

	gc.MoveTo(10, 10)
	gc.LineTo(100, 100)
	gc.QuadCurveTo(100, 10, 10, 10)
	gc.Close()

	invertY(gc, height)
	gc.Stroke()

	filename := "sample.pdf"
	err := draw2dpdf.SaveToPdfFile(filename, dst)
	check(err)
}

// Generates PNG output.
func genPNG() {
	width, height := 800, 600
	dst := image.NewRGBA(image.Rect(0, 0, width, height))
	gc := draw2dimg.NewGraphicContext(dst)

	gc.SetStrokeColor(black)
	gc.SetLineWidth(2)

	gc.MoveTo(10, 10)
	gc.LineTo(100, 100)
	gc.QuadCurveTo(100, 10, 10, 10)
	gc.Close()

	invertY(gc, height)
	gc.Stroke()

	filename := "sample.png"
	err := draw2dimg.SaveToPngFile(filename, dst)
	check(err)
}

func main() {
	genPNG()
	genPDF()
}

regards

llgcode avatar Jan 08 '18 16:01 llgcode

Thanks for the tips. This is taken from the gofpdf package documentation:

go doc gofpdf.TransformBegin

func (f *Fpdf) TransformBegin()
    TransformBegin sets up a transformation context for subsequent text,
    drawings and images. The typical usage is to immediately follow a call to
    this method with a call to one or more of the transformation methods such as
    TransformScale(), TransformSkew(), etc. This is followed by text, drawing or
    image output and finally a call to TransformEnd(). All transformation
    contexts must be properly ended prior to outputting the document.

So, i applied the TransformBegin() and TransformEnd() functions as told in the the documentation and it works:

// Generates PDF output.
func genPDF() {
	height := 600
	dst := draw2dpdf.NewPdf("L", "mm", "A4")
	gc := draw2dpdf.NewGraphicContext(dst)
	dst.TransformBegin()
	invertY(gc, height)
	gc.SetStrokeColor(black)
	gc.SetLineWidth(2)
	
	gc.MoveTo(10, 10)
	gc.LineTo(100, 100)
	gc.QuadCurveTo(100, 10, 10, 10)
	gc.Close()
	dst.TransformEnd()
	gc.Stroke()

	filename := "sample.pdf"
	err := draw2dpdf.SaveToPdfFile(filename, dst)
	check(err)
}

I believe it would be more straightforward if both TransformBegin() and TransformEnd() functions were accessible via gc -- *draw2dpdf.GraphicContext. Also, i should note that it's necessary to apply dst.TransformEnd() function just before gc.Stroke(). Thanks for pointing me in the right direction.

koraye80 avatar Jan 08 '18 22:01 koraye80