imagetype.go 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. package imagetype
  2. import (
  3. "fmt"
  4. "net/url"
  5. "path/filepath"
  6. "strings"
  7. )
  8. type Type int
  9. const (
  10. Unknown Type = iota
  11. JPEG
  12. JXL
  13. PNG
  14. WEBP
  15. GIF
  16. ICO
  17. SVG
  18. HEIC
  19. AVIF
  20. BMP
  21. TIFF
  22. )
  23. const (
  24. contentDispositionFilenameFallback = "image"
  25. contentDispositionsFmt = "%s; filename=\"%s%s\""
  26. )
  27. var (
  28. Types = map[string]Type{
  29. "jpeg": JPEG,
  30. "jpg": JPEG,
  31. "jxl": JXL,
  32. "png": PNG,
  33. "webp": WEBP,
  34. "gif": GIF,
  35. "ico": ICO,
  36. "svg": SVG,
  37. "heic": HEIC,
  38. "avif": AVIF,
  39. "bmp": BMP,
  40. "tiff": TIFF,
  41. }
  42. mimes = map[Type]string{
  43. JPEG: "image/jpeg",
  44. JXL: "image/jxl",
  45. PNG: "image/png",
  46. WEBP: "image/webp",
  47. GIF: "image/gif",
  48. ICO: "image/x-icon",
  49. SVG: "image/svg+xml",
  50. HEIC: "image/heif",
  51. AVIF: "image/avif",
  52. BMP: "image/bmp",
  53. TIFF: "image/tiff",
  54. }
  55. extensions = map[Type]string{
  56. JPEG: ".jpg",
  57. JXL: ".jxl",
  58. PNG: ".png",
  59. WEBP: ".webp",
  60. GIF: ".gif",
  61. ICO: ".ico",
  62. SVG: ".svg",
  63. HEIC: ".heic",
  64. AVIF: ".avif",
  65. BMP: ".bmp",
  66. TIFF: ".tiff",
  67. }
  68. )
  69. func ByMime(mime string) Type {
  70. for k, v := range mimes {
  71. if v == mime {
  72. return k
  73. }
  74. }
  75. return Unknown
  76. }
  77. func (it Type) String() string {
  78. // JPEG has two names, we should use only the full one
  79. if it == JPEG {
  80. return "jpeg"
  81. }
  82. for k, v := range Types {
  83. if v == it {
  84. return k
  85. }
  86. }
  87. return ""
  88. }
  89. func (it Type) Ext() string {
  90. if ext, ok := extensions[it]; ok {
  91. return ext
  92. }
  93. return ""
  94. }
  95. func (it Type) MarshalJSON() ([]byte, error) {
  96. for k, v := range Types {
  97. if v == it {
  98. return []byte(fmt.Sprintf("%q", k)), nil
  99. }
  100. }
  101. return []byte("null"), nil
  102. }
  103. func (it Type) Mime() string {
  104. if mime, ok := mimes[it]; ok {
  105. return mime
  106. }
  107. return "application/octet-stream"
  108. }
  109. func (it Type) ContentDisposition(filename string, returnAttachment bool) string {
  110. return ContentDisposition(filename, it.Ext(), returnAttachment)
  111. }
  112. func (it Type) ContentDispositionFromURL(imageURL string, returnAttachment bool) string {
  113. url, err := url.Parse(imageURL)
  114. if err != nil {
  115. return it.ContentDisposition(contentDispositionFilenameFallback, returnAttachment)
  116. }
  117. _, filename := filepath.Split(url.Path)
  118. if len(filename) == 0 {
  119. return it.ContentDisposition(contentDispositionFilenameFallback, returnAttachment)
  120. }
  121. return it.ContentDisposition(strings.TrimSuffix(filename, filepath.Ext(filename)), returnAttachment)
  122. }
  123. func (it Type) IsVector() bool {
  124. return it == SVG
  125. }
  126. func (it Type) SupportsAlpha() bool {
  127. return it != JPEG && it != BMP
  128. }
  129. func (it Type) SupportsAnimationLoad() bool {
  130. return it == GIF || it == WEBP || it == JXL
  131. }
  132. func (it Type) SupportsAnimationSave() bool {
  133. return it == GIF || it == WEBP
  134. }
  135. func (it Type) SupportsColourProfile() bool {
  136. return it == JPEG ||
  137. it == JXL ||
  138. it == PNG ||
  139. it == WEBP ||
  140. it == HEIC ||
  141. it == AVIF
  142. }
  143. func (it Type) SupportsQuality() bool {
  144. return it == JPEG ||
  145. it == WEBP ||
  146. it == HEIC ||
  147. it == AVIF ||
  148. it == TIFF
  149. }
  150. func (it Type) SupportsThumbnail() bool {
  151. return it == HEIC || it == AVIF
  152. }
  153. func ContentDisposition(filename, ext string, returnAttachment bool) string {
  154. disposition := "inline"
  155. if returnAttachment {
  156. disposition = "attachment"
  157. }
  158. return fmt.Sprintf(contentDispositionsFmt, disposition, strings.ReplaceAll(filename, `"`, "%22"), ext)
  159. }