johnfercher / maroto

A maroto way to create PDFs. Maroto is inspired in Bootstrap and uses gofpdf. Fast and simple.
https://maroto.io
MIT License
1.48k stars 172 forks source link

Support EXIF rotation data #396

Open andoks opened 3 months ago

andoks commented 3 months ago

Is your feature request related to a problem? Please describe.

When i embed images using image.NewFromBytes(image, type, props), they are sometimes displayed in the wrong orientation.

Describe the solution you'd like

Assuming the reason is that the embedding does not take exif orientation information into account, and that is the reason this happens; I would like maroto to be able to take exif orientation information into account from the image data.

Describe alternatives you've considered

Additional context

johnfercher commented 3 months ago

Maroto uses gofpdf under the hood, idk if gofpdf supports this.

andoks commented 3 months ago

I solved it by using goexif like so, borrowed from stack overflow somewhere I think:

func readJPEG(rs io.ReadSeeker) (JPEG, error) {
    img, err := jpeg.Decode(rs)
    if err != nil {
        return nil, fmt.Errorf("faile to decode image as JPEG, error was: %w", err)
    }
    _, err = rs.Seek(0, io.SeekStart)

    // deal with exif
    x, err := exif.Decode(rs)
    if err != nil && x != nil {
        return nil, fmt.Errorf("failed reading exif information from image, error was: %w", err)
    }
    if x != nil {
        orient, _ := x.Get(exif.Orientation)
        if orient != nil {
            img = reverseOrientation(img, orient.String())
        } else {
            img = reverseOrientation(img, "1")
        }
    }

    var buf bytes.Buffer
    err = jpeg.Encode(&buf, img, nil)
    if err != nil {
        return nil, fmt.Errorf("failed encode image after exif-orientation adjusting it, error was: %w", err)
    }

    return buf.Bytes(), nil
}

func reverseOrientation(img image.Image, o string) *image.NRGBA {
    switch o {
    case "1":
        return imaging.Clone(img)
    case "2":
        return imaging.FlipV(img)
    case "3":
        return imaging.Rotate180(img)
    case "4":
        return imaging.Rotate180(imaging.FlipV(img))
    case "5":
        return imaging.Rotate270(imaging.FlipV(img))
    case "6":
        return imaging.Rotate270(img)
    case "7":
        return imaging.Rotate90(imaging.FlipV(img))
    case "8":
        return imaging.Rotate90(img)
    }
    slog.Error("unknown orientation, expect 1-8", slog.String("orientation", o))
    return imaging.Clone(img)
}