Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[full-ci] Thumbnails for GGP files #10304

Merged
merged 3 commits into from
Nov 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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.