Skip to content

Commit

Permalink
Merge pull request #10304 from kobergj/BumpReva
Browse files Browse the repository at this point in the history
[full-ci] Thumbnails for GGP files
  • Loading branch information
micbar authored Nov 7, 2024
2 parents c8e7c2c + 2b8b853 commit c9b37ba
Show file tree
Hide file tree
Showing 8 changed files with 91 additions and 24 deletions.
5 changes: 5 additions & 0 deletions changelog/unreleased/ggp-mimetype-thumbnails.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Enhancement: Create thumbnails for GGP MIME types

Creates thumbnails for newly added ggp files

https://github.com/owncloud/ocis/pull/10303
2 changes: 2 additions & 0 deletions services/thumbnails/pkg/errors/error.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ var (
ErrInvalidType = errors.New("thumbnails: can't encode this type")
// ErrNoEncoderForType represents the error when an encoder couldn't be found for a type.
ErrNoEncoderForType = errors.New("thumbnails: no encoder for this type found")
// ErrNoGeneratorForType represents the error when a generator couldn't be found for a type.
ErrNoGeneratorForType = errors.New("thumbnails: no generator for this type found")
// ErrNoImageFromAudioFile defines an error when an image cannot be extracted from an audio file
ErrNoImageFromAudioFile = errors.New("thumbnails: could not extract image from audio file")
// ErrNoConverterForExtractedImageFromGgsFile defines an error when the extracted image from an ggs file could not be converted
Expand Down
64 changes: 59 additions & 5 deletions services/thumbnails/pkg/preprocessor/preprocessor.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import (
"archive/zip"
"bufio"
"bytes"
"encoding/base64"
"encoding/json"
"image"
"image/draw"
"image/gif"
Expand Down Expand Up @@ -39,11 +41,10 @@ func (i GifDecoder) Convert(r io.Reader) (interface{}, error) {
}

// GgsDecoder is a converter for the geogebra slides file
type GgsDecoder struct{}
type GgsDecoder struct{ thumbnailpath string }

// Convert reads the ggs file and returns the thumbnail image
func (g GgsDecoder) Convert(r io.Reader) (interface{}, error) {
geogebraThumbnail := "_slide0/geogebra_thumbnail.png"
var buf bytes.Buffer
_, err := io.Copy(&buf, r)
if err != nil {
Expand All @@ -54,7 +55,7 @@ func (g GgsDecoder) Convert(r io.Reader) (interface{}, error) {
return nil, err
}
for _, file := range zipReader.File {
if file.Name == geogebraThumbnail {
if file.Name == g.thumbnailpath {
thumbnail, err := file.Open()
if err != nil {
return nil, err
Expand All @@ -70,7 +71,7 @@ func (g GgsDecoder) Convert(r io.Reader) (interface{}, error) {
return img, nil
}
}
return nil, errors.Errorf("%s not found", geogebraThumbnail)
return nil, errors.Errorf("%s not found", g.thumbnailpath)
}

// AudioDecoder is a converter for the audio file
Expand Down Expand Up @@ -177,6 +178,57 @@ Scan: // Label for the scanner loop, so we can break it easily
return img, scanner.Err()
}

// GGPStruct is the layout of a ggp file (which is basically json)
type GGPStruct struct {
Sections []struct {
Cards []struct {
Element struct {
Image struct {
Base64Image string
}
}
}
}
}

// GgpDecoder is a converter for the geogebra pinboard file
type GgpDecoder struct{}

// Convert reads the ggp file and returns the first thumbnail image
func (j GgpDecoder) Convert(r io.Reader) (interface{}, error) {
ggp := &GGPStruct{}
err := json.NewDecoder(r).Decode(ggp)
if err != nil {
return nil, err
}

elem, err := extractBase64ImageFromGGP(ggp)
if err != nil {
return nil, err
}

b, err := base64.StdEncoding.DecodeString(elem)
if err != nil {
return nil, err
}

img, _, err := image.Decode(bytes.NewReader(b))
return img, err
}

func extractBase64ImageFromGGP(ggp *GGPStruct) (string, error) {
if len(ggp.Sections) < 1 || len(ggp.Sections[0].Cards) < 1 {
return "", errors.New("cant find thumbnail in ggp file")
}

raw := strings.Split(ggp.Sections[0].Cards[0].Element.Image.Base64Image, "base64,")
if len(raw) < 2 {
return "", errors.New("cant decode ggp thumbnail")
}

return raw[1], nil
}

// Draw the word in the canvas. The mixX and maxX defines the drawable range
// (X axis) where the word can be drawn (in case the word is too big and doesn't
// fit in the canvas), and the incY defines the increment in the Y axis if we
Expand Down Expand Up @@ -258,7 +310,9 @@ func ForType(mimeType string, opts map[string]interface{}) FileConverter {
fontLoader: fontLoader,
}
case "application/vnd.geogebra.slides":
return GgsDecoder{}
return GgsDecoder{"_slide0/geogebra_thumbnail.png"}
case "application/vnd.geogebra.pinboard":
return GgpDecoder{}
case "image/gif":
return GifDecoder{}
case "audio/flac":
Expand Down
9 changes: 5 additions & 4 deletions services/thumbnails/pkg/preprocessor/preprocessor_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@ package preprocessor

import (
"bytes"
"golang.org/x/image/font"
"golang.org/x/image/font/opentype"
"io"
"os"
"testing"

"golang.org/x/image/font"
"golang.org/x/image/font/opentype"

. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)
Expand Down Expand Up @@ -80,14 +81,14 @@ var _ = Describe("ImageDecoder", func() {
})

It("should decode a ggs", func() {
decoder := GgsDecoder{}
decoder := GgsDecoder{"_slide0/geogebra_thumbnail.png"}
img, err := decoder.Convert(fileReader)
Expect(err).ToNot(HaveOccurred())
Expect(img).ToNot(BeNil())
})

It("should return an error if the ggs is invalid", func() {
decoder := GgsDecoder{}
decoder := GgsDecoder{"_slide0/geogebra_thumbnail.png"}
img, err := decoder.Convert(bytes.NewReader([]byte("not a ggs")))
Expect(err).To(HaveOccurred())
Expect(img).To(BeNil())
Expand Down
5 changes: 4 additions & 1 deletion services/thumbnails/pkg/thumbnail/encoding.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ const (
typeJpeg = "jpeg"
typeGif = "gif"
typeGgs = "ggs"
typeGgp = "ggp"
)

// Encoder encodes the thumbnail to a specific format.
Expand Down Expand Up @@ -52,7 +53,7 @@ func (e GifEncoder) MimeType() string {
// or nil if the type is not supported.
func EncoderForType(fileType string) (Encoder, error) {
switch strings.ToLower(fileType) {
case typePng, typeGgs:
case typePng, typeGgs, typeGgp:
return PngEncoder{}, nil
case typeJpg, typeJpeg:
return JpegEncoder{}, nil
Expand All @@ -71,6 +72,8 @@ func GetExtForMime(fileType string) string {
return ext
case "application/vnd.geogebra.slides":
return typeGgs
case "application/vnd.geogebra.pinboard":
return typeGgp
default:
return ""
}
Expand Down
4 changes: 2 additions & 2 deletions services/thumbnails/pkg/thumbnail/generator.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,11 +89,11 @@ func (g GifGenerator) imageToPaletted(img image.Image, p color.Palette) *image.P
// or nil if the type is not supported.
func GeneratorFor(fileType, processorID string) (Generator, error) {
switch strings.ToLower(fileType) {
case typePng, typeJpg, typeJpeg, typeGgs:
case typePng, typeJpg, typeJpeg, typeGgs, typeGgp:
return NewSimpleGenerator(fileType, processorID)
case typeGif:
return NewGifGenerator(fileType, processorID)
default:
return nil, errors.ErrNoEncoderForType
return nil, errors.ErrNoGeneratorForType
}
}
25 changes: 13 additions & 12 deletions services/thumbnails/pkg/thumbnail/thumbnail.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,19 @@ import (
var (
// SupportedMimeTypes contains an all mimetypes which are supported by the thumbnailer.
SupportedMimeTypes = map[string]struct{}{
"image/png": {},
"image/jpg": {},
"image/jpeg": {},
"image/gif": {},
"image/bmp": {},
"image/x-ms-bmp": {},
"image/tiff": {},
"text/plain": {},
"audio/flac": {},
"audio/mpeg": {},
"audio/ogg": {},
"application/vnd.geogebra.slides": {},
"image/png": {},
"image/jpg": {},
"image/jpeg": {},
"image/gif": {},
"image/bmp": {},
"image/x-ms-bmp": {},
"image/tiff": {},
"text/plain": {},
"audio/flac": {},
"audio/mpeg": {},
"audio/ogg": {},
"application/vnd.geogebra.slides": {},
"application/vnd.geogebra.pinboard": {},
}
)

Expand Down
1 change: 1 addition & 0 deletions services/thumbnails/testdata/test.ggp

Large diffs are not rendered by default.

0 comments on commit c9b37ba

Please sign in to comment.