imagetype.go 2.8 KB

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