mirror of
https://github.com/celogeek/go-comic-converter.git
synced 2025-05-25 08:12:36 +02:00
cover improvement
include part in cover if totalParts > 1 cleaner cover in grayscale
This commit is contained in:
parent
4a1ae516ea
commit
0de6cc9acd
@ -99,6 +99,49 @@ func (e *ePub) writeBlank(wz *epubzip.EPUBZip, img *epubimage.Image) error {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// write title image
|
||||||
|
func (e *ePub) writeCoverImage(wz *epubzip.EPUBZip, img *epubimage.Image, part, totalParts int) error {
|
||||||
|
title := "Cover"
|
||||||
|
text := ""
|
||||||
|
if totalParts > 1 {
|
||||||
|
text = fmt.Sprintf("%d / %d", part, totalParts)
|
||||||
|
title = fmt.Sprintf("%s %s", title, text)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := wz.WriteContent(
|
||||||
|
"OEBPS/Text/cover.xhtml",
|
||||||
|
[]byte(e.render(epubtemplates.Text, map[string]any{
|
||||||
|
"Title": title,
|
||||||
|
"ViewPort": fmt.Sprintf("width=%d,height=%d", e.Image.View.Width, e.Image.View.Height),
|
||||||
|
"ImagePath": fmt.Sprintf("Images/cover.%s", e.Image.Format),
|
||||||
|
"ImageStyle": img.ImgStyle(e.Image.View.Width, e.Image.View.Height, ""),
|
||||||
|
})),
|
||||||
|
); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
coverTitle, err := e.imageProcessor.CoverTitleData(&epubimageprocessor.CoverTitleDataOptions{
|
||||||
|
Src: img.Raw,
|
||||||
|
Name: "cover",
|
||||||
|
Text: text,
|
||||||
|
Align: "bottom",
|
||||||
|
PctWidth: 50,
|
||||||
|
PctMargin: 50,
|
||||||
|
MaxFontSize: 96,
|
||||||
|
BorderSize: 8,
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := wz.WriteRaw(coverTitle); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// write title image
|
// write title image
|
||||||
func (e *ePub) writeTitleImage(wz *epubzip.EPUBZip, img *epubimage.Image, title string) error {
|
func (e *ePub) writeTitleImage(wz *epubzip.EPUBZip, img *epubimage.Image, title string) error {
|
||||||
titleAlign := ""
|
titleAlign := ""
|
||||||
@ -134,7 +177,16 @@ func (e *ePub) writeTitleImage(wz *epubzip.EPUBZip, img *epubimage.Image, title
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
coverTitle, err := e.imageProcessor.CoverTitleData(img.Raw, title)
|
coverTitle, err := e.imageProcessor.CoverTitleData(&epubimageprocessor.CoverTitleDataOptions{
|
||||||
|
Src: img.Raw,
|
||||||
|
Name: "title",
|
||||||
|
Text: title,
|
||||||
|
Align: "center",
|
||||||
|
PctWidth: 100,
|
||||||
|
PctMargin: 100,
|
||||||
|
MaxFontSize: 64,
|
||||||
|
BorderSize: 4,
|
||||||
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -184,11 +236,8 @@ func (e *ePub) getParts() (parts []*epubPart, imgStorage *epubzip.EPUBZipStorage
|
|||||||
// compute size of the EPUB part and try to be as close as possible of the target
|
// compute size of the EPUB part and try to be as close as possible of the target
|
||||||
maxSize := uint64(e.LimitMb * 1024 * 1024)
|
maxSize := uint64(e.LimitMb * 1024 * 1024)
|
||||||
xhtmlSize := uint64(1024)
|
xhtmlSize := uint64(1024)
|
||||||
// descriptor files + title
|
// descriptor files + title + cover
|
||||||
baseSize := uint64(16*1024) + imgStorage.Size(cover.EPUBImgPath())
|
baseSize := uint64(16*1024) + imgStorage.Size(cover.EPUBImgPath())*2
|
||||||
if e.Image.HasCover {
|
|
||||||
baseSize += imgStorage.Size(cover.EPUBImgPath())
|
|
||||||
}
|
|
||||||
|
|
||||||
currentSize := baseSize
|
currentSize := baseSize
|
||||||
currentImages := make([]*epubimage.Image, 0)
|
currentImages := make([]*epubimage.Image, 0)
|
||||||
@ -203,9 +252,6 @@ func (e *ePub) getParts() (parts []*epubPart, imgStorage *epubzip.EPUBZipStorage
|
|||||||
})
|
})
|
||||||
part += 1
|
part += 1
|
||||||
currentSize = baseSize
|
currentSize = baseSize
|
||||||
if !e.Image.HasCover {
|
|
||||||
currentSize += imgStorage.Size(cover.EPUBImgPath())
|
|
||||||
}
|
|
||||||
currentImages = make([]*epubimage.Image, 0)
|
currentImages = make([]*epubimage.Image, 0)
|
||||||
}
|
}
|
||||||
currentSize += imgSize
|
currentSize += imgSize
|
||||||
@ -377,16 +423,12 @@ func (e *ePub) Write() error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = e.writeTitleImage(wz, part.Cover, title); err != nil {
|
if err = e.writeCoverImage(wz, part.Cover, i+1, totalParts); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cover exist or part > 1
|
if err = e.writeTitleImage(wz, part.Cover, title); err != nil {
|
||||||
// If no cover, part 2 and more will include the image as a cover
|
return err
|
||||||
if e.Image.HasCover || i > 0 {
|
|
||||||
if err := e.writeImage(wz, part.Cover, imgStorage.Get(part.Cover.EPUBImgPath())); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
lastImage := part.Images[len(part.Images)-1]
|
lastImage := part.Images[len(part.Images)-1]
|
||||||
|
@ -12,12 +12,17 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// Create a title with the cover image
|
// Create a title with the cover image
|
||||||
func CoverTitle(title string) gift.Filter {
|
func CoverTitle(title string, align string, pctWidth int, pctMargin int, maxFontSize int, borderSize int) gift.Filter {
|
||||||
return &coverTitle{title}
|
return &coverTitle{title, align, pctWidth, pctMargin, maxFontSize, borderSize}
|
||||||
}
|
}
|
||||||
|
|
||||||
type coverTitle struct {
|
type coverTitle struct {
|
||||||
title string
|
title string
|
||||||
|
align string
|
||||||
|
pctWidth int
|
||||||
|
pctMargin int
|
||||||
|
maxFontSize int
|
||||||
|
borderSize int
|
||||||
}
|
}
|
||||||
|
|
||||||
// size is the same as source
|
// size is the same as source
|
||||||
@ -28,28 +33,36 @@ func (p *coverTitle) Bounds(srcBounds image.Rectangle) (dstBounds image.Rectangl
|
|||||||
// blur the src image, and create a box with the title in the middle
|
// blur the src image, and create a box with the title in the middle
|
||||||
func (p *coverTitle) Draw(dst draw.Image, src image.Image, options *gift.Options) {
|
func (p *coverTitle) Draw(dst draw.Image, src image.Image, options *gift.Options) {
|
||||||
draw.Draw(dst, dst.Bounds(), src, src.Bounds().Min, draw.Src)
|
draw.Draw(dst, dst.Bounds(), src, src.Bounds().Min, draw.Src)
|
||||||
|
if p.title == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
srcWidth, srcHeight := src.Bounds().Dx(), src.Bounds().Dy()
|
srcWidth, srcHeight := src.Bounds().Dx(), src.Bounds().Dy()
|
||||||
|
|
||||||
// Calculate size of title
|
// Calculate size of title
|
||||||
f, _ := truetype.Parse(gomonobold.TTF)
|
f, _ := truetype.Parse(gomonobold.TTF)
|
||||||
borderSize := 4
|
|
||||||
var fontSize, textWidth, textHeight int
|
var fontSize, textWidth, textHeight int
|
||||||
for fontSize = 64; fontSize >= 12; fontSize -= 1 {
|
for fontSize = p.maxFontSize; fontSize >= 12; fontSize -= 1 {
|
||||||
face := truetype.NewFace(f, &truetype.Options{Size: float64(fontSize), DPI: 72})
|
face := truetype.NewFace(f, &truetype.Options{Size: float64(fontSize), DPI: 72})
|
||||||
textWidth = font.MeasureString(face, p.title).Ceil()
|
textWidth = font.MeasureString(face, p.title).Ceil()
|
||||||
textHeight = face.Metrics().Ascent.Ceil() + face.Metrics().Descent.Ceil()
|
textHeight = face.Metrics().Ascent.Ceil() + face.Metrics().Descent.Ceil()
|
||||||
if textWidth+2*borderSize < srcWidth && 3*textHeight+2*borderSize < srcHeight {
|
if textWidth+2*p.borderSize < srcWidth*p.pctWidth/100 && 3*textHeight+2*p.borderSize < srcHeight {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw rectangle in the middle of the image
|
// Draw rectangle in the middle of the image
|
||||||
textPosStart := srcHeight/2 - textHeight/2
|
marginSize := fontSize * p.pctMargin / 100
|
||||||
textPosEnd := srcHeight/2 + textHeight/2
|
var textPosStart, textPosEnd int
|
||||||
marginSize := fontSize
|
if p.align == "bottom" {
|
||||||
borderArea := image.Rect(0, textPosStart-borderSize-marginSize, srcWidth, textPosEnd+borderSize+marginSize)
|
textPosStart = srcHeight - textHeight - p.borderSize - marginSize
|
||||||
textArea := image.Rect(borderSize, textPosStart-marginSize, srcWidth-borderSize, textPosEnd+marginSize)
|
textPosEnd = srcHeight - p.borderSize - marginSize
|
||||||
|
} else {
|
||||||
|
textPosStart = srcHeight/2 - textHeight/2
|
||||||
|
textPosEnd = srcHeight/2 + textHeight/2
|
||||||
|
}
|
||||||
|
borderArea := image.Rect((srcWidth-(srcWidth*p.pctWidth/100))/2, textPosStart-p.borderSize-marginSize, (srcWidth+(srcWidth*p.pctWidth/100))/2, textPosEnd+p.borderSize+marginSize)
|
||||||
|
textArea := image.Rect(borderArea.Bounds().Min.X+p.borderSize, textPosStart-marginSize, borderArea.Bounds().Max.X-p.borderSize, textPosEnd+marginSize)
|
||||||
|
|
||||||
draw.Draw(
|
draw.Draw(
|
||||||
dst,
|
dst,
|
||||||
@ -76,9 +89,10 @@ func (p *coverTitle) Draw(dst draw.Image, src image.Image, options *gift.Options
|
|||||||
c.SetDst(dst)
|
c.SetDst(dst)
|
||||||
c.SetSrc(image.Black)
|
c.SetSrc(image.Black)
|
||||||
|
|
||||||
textLeft := srcWidth/2 - textWidth/2
|
textLeft := textArea.Min.X + textArea.Dx()/2 - textWidth/2
|
||||||
if textLeft < borderSize {
|
if textLeft < textArea.Min.X {
|
||||||
textLeft = borderSize
|
textLeft = textArea.Min.X
|
||||||
}
|
}
|
||||||
c.DrawString(p.title, freetype.Pt(textLeft, srcHeight/2+textHeight/4))
|
textTop := textArea.Min.Y + textArea.Dy()/2 + textHeight/4
|
||||||
|
c.DrawString(p.title, freetype.Pt(textLeft, textTop))
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,7 @@ package epubimageprocessor
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"image"
|
"image"
|
||||||
|
"image/color"
|
||||||
"image/draw"
|
"image/draw"
|
||||||
"os"
|
"os"
|
||||||
"sync"
|
"sync"
|
||||||
@ -168,7 +169,7 @@ func (e *EPUBImageProcessor) createImage(src image.Image, r image.Rectangle) dra
|
|||||||
// transform image into 1 or 3 images
|
// transform image into 1 or 3 images
|
||||||
// only doublepage with autosplit has 3 versions
|
// only doublepage with autosplit has 3 versions
|
||||||
func (e *EPUBImageProcessor) transformImage(src image.Image, srcId int) []image.Image {
|
func (e *EPUBImageProcessor) transformImage(src image.Image, srcId int) []image.Image {
|
||||||
var filters, splitFilter []gift.Filter
|
var filters, splitFilters []gift.Filter
|
||||||
var images []image.Image
|
var images []image.Image
|
||||||
|
|
||||||
// Lookup for margin if crop is enable or if we want to remove blank image
|
// Lookup for margin if crop is enable or if we want to remove blank image
|
||||||
@ -188,7 +189,7 @@ func (e *EPUBImageProcessor) transformImage(src image.Image, srcId int) []image.
|
|||||||
// crop is enable or if blank image with noblankimage options
|
// crop is enable or if blank image with noblankimage options
|
||||||
if e.Image.Crop.Enabled || (e.Image.NoBlankImage && isBlank) {
|
if e.Image.Crop.Enabled || (e.Image.NoBlankImage && isBlank) {
|
||||||
filters = append(filters, f)
|
filters = append(filters, f)
|
||||||
splitFilter = append(splitFilter, f)
|
splitFilters = append(splitFilters, f)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -199,13 +200,13 @@ func (e *EPUBImageProcessor) transformImage(src image.Image, srcId int) []image.
|
|||||||
if e.Image.Contrast != 0 {
|
if e.Image.Contrast != 0 {
|
||||||
f := gift.Contrast(float32(e.Image.Contrast))
|
f := gift.Contrast(float32(e.Image.Contrast))
|
||||||
filters = append(filters, f)
|
filters = append(filters, f)
|
||||||
splitFilter = append(splitFilter, f)
|
splitFilters = append(splitFilters, f)
|
||||||
}
|
}
|
||||||
|
|
||||||
if e.Image.Brightness != 0 {
|
if e.Image.Brightness != 0 {
|
||||||
f := gift.Brightness(float32(e.Image.Brightness))
|
f := gift.Brightness(float32(e.Image.Brightness))
|
||||||
filters = append(filters, f)
|
filters = append(filters, f)
|
||||||
splitFilter = append(splitFilter, f)
|
splitFilters = append(splitFilters, f)
|
||||||
}
|
}
|
||||||
|
|
||||||
if e.Image.Resize {
|
if e.Image.Resize {
|
||||||
@ -213,6 +214,11 @@ func (e *EPUBImageProcessor) transformImage(src image.Image, srcId int) []image.
|
|||||||
filters = append(filters, f)
|
filters = append(filters, f)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if e.Image.GrayScale {
|
||||||
|
filters = append(filters, gift.Grayscale())
|
||||||
|
splitFilters = append(splitFilters, gift.Grayscale())
|
||||||
|
}
|
||||||
|
|
||||||
filters = append(filters, epubimagefilters.Pixel())
|
filters = append(filters, epubimagefilters.Pixel())
|
||||||
|
|
||||||
// convert
|
// convert
|
||||||
@ -240,7 +246,7 @@ func (e *EPUBImageProcessor) transformImage(src image.Image, srcId int) []image.
|
|||||||
|
|
||||||
// convert double page
|
// convert double page
|
||||||
for _, b := range []bool{e.Image.Manga, !e.Image.Manga} {
|
for _, b := range []bool{e.Image.Manga, !e.Image.Manga} {
|
||||||
g := gift.New(splitFilter...)
|
g := gift.New(splitFilters...)
|
||||||
g.Add(epubimagefilters.CropSplitDoublePage(b))
|
g.Add(epubimagefilters.CropSplitDoublePage(b))
|
||||||
if e.Image.Resize {
|
if e.Image.Resize {
|
||||||
g.Add(gift.ResizeToFit(e.Image.View.Width, e.Image.View.Height, gift.LanczosResampling))
|
g.Add(gift.ResizeToFit(e.Image.View.Width, e.Image.View.Height, gift.LanczosResampling))
|
||||||
@ -253,15 +259,52 @@ func (e *EPUBImageProcessor) transformImage(src image.Image, srcId int) []image.
|
|||||||
return images
|
return images
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type CoverTitleDataOptions struct {
|
||||||
|
Src image.Image
|
||||||
|
Name string
|
||||||
|
Text string
|
||||||
|
Align string
|
||||||
|
PctWidth int
|
||||||
|
PctMargin int
|
||||||
|
MaxFontSize int
|
||||||
|
BorderSize int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *EPUBImageProcessor) Cover16LevelOfGray(bounds image.Rectangle) draw.Image {
|
||||||
|
return image.NewPaletted(bounds, color.Palette{
|
||||||
|
color.Gray{0x00},
|
||||||
|
color.Gray{0x11},
|
||||||
|
color.Gray{0x22},
|
||||||
|
color.Gray{0x33},
|
||||||
|
color.Gray{0x44},
|
||||||
|
color.Gray{0x55},
|
||||||
|
color.Gray{0x66},
|
||||||
|
color.Gray{0x77},
|
||||||
|
color.Gray{0x88},
|
||||||
|
color.Gray{0x99},
|
||||||
|
color.Gray{0xAA},
|
||||||
|
color.Gray{0xBB},
|
||||||
|
color.Gray{0xCC},
|
||||||
|
color.Gray{0xDD},
|
||||||
|
color.Gray{0xEE},
|
||||||
|
color.Gray{0xFF},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// create a title page with the cover
|
// create a title page with the cover
|
||||||
func (e *EPUBImageProcessor) CoverTitleData(src image.Image, title string) (*epubzip.ZipImage, error) {
|
func (e *EPUBImageProcessor) CoverTitleData(o *CoverTitleDataOptions) (*epubzip.ZipImage, error) {
|
||||||
// Create a blur version of the cover
|
// Create a blur version of the cover
|
||||||
g := gift.New(epubimagefilters.CoverTitle(title))
|
g := gift.New(epubimagefilters.CoverTitle(o.Text, o.Align, o.PctWidth, o.PctMargin, o.MaxFontSize, o.BorderSize))
|
||||||
dst := e.createImage(src, g.Bounds(src.Bounds()))
|
var dst draw.Image
|
||||||
g.Draw(dst, src)
|
if o.Name == "cover" && e.Image.GrayScale {
|
||||||
|
dst = e.Cover16LevelOfGray(o.Src.Bounds())
|
||||||
|
} else {
|
||||||
|
dst = e.createImage(o.Src, g.Bounds(o.Src.Bounds()))
|
||||||
|
}
|
||||||
|
g.Draw(dst, o.Src)
|
||||||
|
|
||||||
return epubzip.CompressImage(
|
return epubzip.CompressImage(
|
||||||
fmt.Sprintf("OEBPS/Images/title.%s", e.Image.Format),
|
fmt.Sprintf("OEBPS/Images/%s.%s", o.Name, e.Image.Format),
|
||||||
e.Image.Format,
|
e.Image.Format,
|
||||||
dst,
|
dst,
|
||||||
e.Image.Quality,
|
e.Image.Quality,
|
||||||
|
@ -124,9 +124,7 @@ func getMeta(o *ContentOptions) []tag {
|
|||||||
metas = append(metas, tag{"meta", tagAttrs{"name": "primary-writing-mode", "content": "horizontal-lr"}, ""})
|
metas = append(metas, tag{"meta", tagAttrs{"name": "primary-writing-mode", "content": "horizontal-lr"}, ""})
|
||||||
}
|
}
|
||||||
|
|
||||||
if o.Cover != nil {
|
metas = append(metas, tag{"meta", tagAttrs{"name": "cover", "content": "img_cover"}, ""})
|
||||||
metas = append(metas, tag{"meta", tagAttrs{"name": "cover", "content": o.Cover.ImgKey()}, ""})
|
|
||||||
}
|
|
||||||
|
|
||||||
if o.Total > 1 {
|
if o.Total > 1 {
|
||||||
metas = append(
|
metas = append(
|
||||||
@ -160,16 +158,14 @@ func getManifest(o *ContentOptions) []tag {
|
|||||||
{"item", tagAttrs{"id": "css", "href": "Text/style.css", "media-type": "text/css"}, ""},
|
{"item", tagAttrs{"id": "css", "href": "Text/style.css", "media-type": "text/css"}, ""},
|
||||||
{"item", tagAttrs{"id": "page_title", "href": "Text/title.xhtml", "media-type": "application/xhtml+xml"}, ""},
|
{"item", tagAttrs{"id": "page_title", "href": "Text/title.xhtml", "media-type": "application/xhtml+xml"}, ""},
|
||||||
{"item", tagAttrs{"id": "img_title", "href": fmt.Sprintf("Images/title.%s", o.ImageOptions.Format), "media-type": fmt.Sprintf("image/%s", o.ImageOptions.Format)}, ""},
|
{"item", tagAttrs{"id": "img_title", "href": fmt.Sprintf("Images/title.%s", o.ImageOptions.Format), "media-type": fmt.Sprintf("image/%s", o.ImageOptions.Format)}, ""},
|
||||||
|
{"item", tagAttrs{"id": "page_cover", "href": "Text/cover.xhtml", "media-type": "application/xhtml+xml"}, ""},
|
||||||
|
{"item", tagAttrs{"id": "img_cover", "href": fmt.Sprintf("Images/cover.%s", o.ImageOptions.Format), "media-type": fmt.Sprintf("image/%s", o.ImageOptions.Format)}, ""},
|
||||||
}
|
}
|
||||||
|
|
||||||
if !o.ImageOptions.View.PortraitOnly {
|
if !o.ImageOptions.View.PortraitOnly {
|
||||||
items = append(items, tag{"item", tagAttrs{"id": "space_title", "href": "Text/space_title.xhtml", "media-type": "application/xhtml+xml"}, ""})
|
items = append(items, tag{"item", tagAttrs{"id": "space_title", "href": "Text/space_title.xhtml", "media-type": "application/xhtml+xml"}, ""})
|
||||||
}
|
}
|
||||||
|
|
||||||
if o.ImageOptions.HasCover || o.Current > 1 {
|
|
||||||
addTag(o.Cover, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
lastImage := o.Images[len(o.Images)-1]
|
lastImage := o.Images[len(o.Images)-1]
|
||||||
for _, img := range o.Images {
|
for _, img := range o.Images {
|
||||||
addTag(img, !o.ImageOptions.View.PortraitOnly && (img.DoublePage || (img.Part == 0 && img == lastImage)))
|
addTag(img, !o.ImageOptions.View.PortraitOnly && (img.DoublePage || (img.Part == 0 && img == lastImage)))
|
||||||
@ -249,10 +245,8 @@ func getSpinePortrait(o *ContentOptions) []tag {
|
|||||||
|
|
||||||
// guide part of the content
|
// guide part of the content
|
||||||
func getGuide(o *ContentOptions) []tag {
|
func getGuide(o *ContentOptions) []tag {
|
||||||
guide := []tag{}
|
return []tag{
|
||||||
if o.Cover != nil {
|
{"reference", tagAttrs{"type": "cover", "title": "cover", "href": "Text/cover.xhtml"}, ""},
|
||||||
guide = append(guide, tag{"reference", tagAttrs{"type": "cover", "title": "cover", "href": o.Cover.PagePath()}, ""})
|
{"reference", tagAttrs{"type": "text", "title": "content", "href": o.Images[0].PagePath()}, ""},
|
||||||
}
|
}
|
||||||
guide = append(guide, tag{"reference", tagAttrs{"type": "text", "title": "content", "href": o.Images[0].PagePath()}, ""})
|
|
||||||
return guide
|
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user