From 18a5611a4a73d7ce9c540eaf6661307fdff165c2 Mon Sep 17 00:00:00 2001 From: celogeek <65178+celogeek@users.noreply.github.com> Date: Wed, 28 Dec 2022 19:45:06 +0100 Subject: [PATCH] split image data / zip --- internal/epub/core.go | 116 ++++-------------------------------- internal/epub/image_data.go | 44 ++++++++++++++ internal/epub/zip.go | 83 ++++++++++++++++++++++++++ 3 files changed, 140 insertions(+), 103 deletions(-) create mode 100644 internal/epub/image_data.go create mode 100644 internal/epub/zip.go diff --git a/internal/epub/core.go b/internal/epub/core.go index d3e4f61..9bd788f 100644 --- a/internal/epub/core.go +++ b/internal/epub/core.go @@ -1,13 +1,8 @@ package epub import ( - "archive/zip" - "bytes" - "compress/flate" "fmt" - "hash/crc32" "io/fs" - "os" "path/filepath" "runtime" "sort" @@ -22,11 +17,6 @@ import ( imageconverter "go-comic-converter/internal/image-converter" ) -type ImageData struct { - Header *zip.FileHeader - Data []byte -} - type Image struct { Id int Data *ImageData @@ -119,60 +109,6 @@ func (e *EPub) SetLimitMb(l int) *EPub { return e } -func (e *EPub) WriteMagic(wz *zip.Writer) error { - t := time.Now() - fh := &zip.FileHeader{ - Name: "mimetype", - Method: zip.Store, - Modified: t, - ModifiedTime: uint16(t.Second()/2 + t.Minute()<<5 + t.Hour()<<11), - ModifiedDate: uint16(t.Day() + int(t.Month())<<5 + (t.Year()-1980)<<9), - CompressedSize64: 20, - UncompressedSize64: 20, - CRC32: 0x2cab616f, - } - fh.SetMode(0600) - m, err := wz.CreateRaw(fh) - - if err != nil { - return err - } - _, err = m.Write([]byte("application/epub+zip")) - return err -} - -func (e *EPub) WriteImage(wz *zip.Writer, image *Image) error { - m, err := wz.CreateRaw(image.Data.Header) - if err != nil { - return err - } - _, err = m.Write(image.Data.Data) - return err -} - -func (e *EPub) WriteFile(wz *zip.Writer, file string, data any) error { - var content []byte - switch b := data.(type) { - case string: - content = []byte(b) - case []byte: - content = b - default: - return fmt.Errorf("support string of []byte") - } - - m, err := wz.CreateHeader(&zip.FileHeader{ - Name: file, - Modified: time.Now(), - Method: zip.Deflate, - }) - if err != nil { - return err - } - _, err = m.Write(content) - return err -} - func (e *EPub) Render(templateString string, data any) string { tmpl, err := e.TemplateProcessor.Parse(templateString) if err != nil { @@ -237,35 +173,13 @@ func (e *EPub) LoadDir(dirname string) *EPub { e.ViewHeight, e.Quality, ) - cdata := bytes.NewBuffer([]byte{}) - wcdata, err := flate.NewWriter(cdata, flate.BestCompression) - if err != nil { - panic(err) - } - wcdata.Write(data) - wcdata.Close() - if err != nil { - panic(err) - } - t := time.Now() name := fmt.Sprintf("OEBPS/Images/%d.jpg", task.Id) if task.Id == 0 { name = "OEBPS/Images/cover.jpg" } results <- &Image{ task.Id, - &ImageData{ - &zip.FileHeader{ - Name: name, - CompressedSize64: uint64(cdata.Len()), - UncompressedSize64: uint64(len(data)), - CRC32: crc32.Checksum(data, crc32.IEEETable), - Method: zip.Deflate, - ModifiedTime: uint16(t.Second()/2 + t.Minute()<<5 + t.Hour()<<11), - ModifiedDate: uint16(t.Day() + int(t.Month())<<5 + (t.Year()-1980)<<9), - }, - cdata.Bytes(), - }, + NewImageData(name, data), w, h, } @@ -313,20 +227,17 @@ func (e *EPub) GetParts() []*EpubPart { maxSize := uint64(e.LimitMb * 1024 * 1024) - sizeImage := func(image *Image) uint64 { - // image + header + len Name + xhtml size - return image.Data.Header.CompressedSize64 + 30 + uint64(len(image.Data.Header.Name)) - } xhtmlSize := uint64(1024) // descriptor files + image - baseSize := uint64(16*1024) + sizeImage(cover) + baseSize := uint64(16*1024) + cover.Data.CompressedSize() currentSize := baseSize currentImages := make([]*Image, 0) part := 1 for _, img := range images { - if len(currentImages) > 0 && currentSize+sizeImage(img)+xhtmlSize > maxSize { + imgSize := img.Data.CompressedSize() + xhtmlSize + if len(currentImages) > 0 && currentSize+imgSize > maxSize { epubPart = append(epubPart, &EpubPart{ Cover: cover, Images: currentImages, @@ -335,7 +246,7 @@ func (e *EPub) GetParts() []*EpubPart { currentSize = baseSize currentImages = make([]*Image, 0) } - currentSize += sizeImage(img) + xhtmlSize + currentSize += imgSize currentImages = append(currentImages, img) } if len(currentImages) > 0 { @@ -368,11 +279,13 @@ func (e *EPub) Write() error { if totalParts > 1 { suffix = fmt.Sprintf(" PART_%02d", i+1) } + path := fmt.Sprintf("%s%s%s", e.Path[0:len(e.Path)-len(ext)], suffix, ext) - w, err := os.Create(path) + wz, err := NewEpubZip(path) if err != nil { return err } + defer wz.Close() zipContent := []ZipContent{ {"META-INF/container.xml", TEMPLATE_CONTAINER}, @@ -387,25 +300,22 @@ func (e *EPub) Write() error { })}, } - wz := zip.NewWriter(w) - defer wz.Close() - - if err = e.WriteMagic(wz); err != nil { + if err = wz.WriteMagic(); err != nil { return err } for _, content := range zipContent { - if err := e.WriteFile(wz, content.Name, content.Content); err != nil { + if err := wz.WriteFile(content.Name, content.Content); err != nil { return err } } - e.WriteImage(wz, part.Cover) + wz.WriteImage(part.Cover.Data) for _, img := range part.Images { text := fmt.Sprintf("OEBPS/Text/%d.xhtml", img.Id) - if err := e.WriteFile(wz, text, e.Render(TEMPLATE_TEXT, img)); err != nil { + if err := wz.WriteFile(text, e.Render(TEMPLATE_TEXT, img)); err != nil { return err } - if err := e.WriteImage(wz, img); err != nil { + if err := wz.WriteImage(img.Data); err != nil { return err } } diff --git a/internal/epub/image_data.go b/internal/epub/image_data.go new file mode 100644 index 0000000..0d61015 --- /dev/null +++ b/internal/epub/image_data.go @@ -0,0 +1,44 @@ +package epub + +import ( + "archive/zip" + "bytes" + "compress/flate" + "hash/crc32" + "time" +) + +type ImageData struct { + Header *zip.FileHeader + Data []byte +} + +func (img *ImageData) CompressedSize() uint64 { + return img.Header.CompressedSize64 + 30 + uint64(len(img.Header.Name)) +} + +func NewImageData(name string, data []byte) *ImageData { + cdata := bytes.NewBuffer([]byte{}) + wcdata, err := flate.NewWriter(cdata, flate.BestCompression) + if err != nil { + panic(err) + } + wcdata.Write(data) + wcdata.Close() + if err != nil { + panic(err) + } + t := time.Now() + return &ImageData{ + &zip.FileHeader{ + Name: name, + CompressedSize64: uint64(cdata.Len()), + UncompressedSize64: uint64(len(data)), + CRC32: crc32.Checksum(data, crc32.IEEETable), + Method: zip.Deflate, + ModifiedTime: uint16(t.Second()/2 + t.Minute()<<5 + t.Hour()<<11), + ModifiedDate: uint16(t.Day() + int(t.Month())<<5 + (t.Year()-1980)<<9), + }, + cdata.Bytes(), + } +} diff --git a/internal/epub/zip.go b/internal/epub/zip.go new file mode 100644 index 0000000..116310d --- /dev/null +++ b/internal/epub/zip.go @@ -0,0 +1,83 @@ +package epub + +import ( + "archive/zip" + "fmt" + "os" + "time" +) + +type EpubZip struct { + w *os.File + wz *zip.Writer +} + +func NewEpubZip(path string) (*EpubZip, error) { + w, err := os.Create(path) + if err != nil { + return nil, err + } + wz := zip.NewWriter(w) + return &EpubZip{w, wz}, nil +} + +func (e *EpubZip) Close() error { + if err := e.wz.Close(); err != nil { + return err + } + return e.w.Close() +} + +func (e *EpubZip) WriteMagic() error { + t := time.Now() + fh := &zip.FileHeader{ + Name: "mimetype", + Method: zip.Store, + Modified: t, + ModifiedTime: uint16(t.Second()/2 + t.Minute()<<5 + t.Hour()<<11), + ModifiedDate: uint16(t.Day() + int(t.Month())<<5 + (t.Year()-1980)<<9), + CompressedSize64: 20, + UncompressedSize64: 20, + CRC32: 0x2cab616f, + } + fh.SetMode(0600) + m, err := e.wz.CreateRaw(fh) + + if err != nil { + return err + } + _, err = m.Write([]byte("application/epub+zip")) + return err +} + +func (e *EpubZip) WriteImage(image *ImageData) error { + m, err := e.wz.CreateRaw(image.Header) + if err != nil { + return err + } + _, err = m.Write(image.Data) + return err +} + +func (e *EpubZip) WriteFile(file string, data any) error { + var content []byte + switch b := data.(type) { + case string: + content = []byte(b) + case []byte: + content = b + default: + return fmt.Errorf("support string of []byte") + } + + m, err := e.wz.CreateHeader(&zip.FileHeader{ + Name: file, + Modified: time.Now(), + Method: zip.Deflate, + }) + if err != nil { + return err + } + _, err = m.Write(content) + return err +}