registry.go 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  1. package imagetype
  2. import (
  3. "io"
  4. "slices"
  5. "github.com/imgproxy/imgproxy/v3/bufreader"
  6. )
  7. const (
  8. // maxDetectionLimit is maximum bytes detectors allowed to read from the source
  9. maxDetectionLimit = 32 * 1024
  10. )
  11. // TypeDesc is used to store metadata about an image type.
  12. // It represents the minimal information needed to make imgproxy to
  13. // work with the type.
  14. type TypeDesc struct {
  15. String string
  16. Ext string
  17. Mime string
  18. IsVector bool
  19. SupportsAlpha bool
  20. SupportsColourProfile bool
  21. SupportsQuality bool
  22. SupportsAnimationLoad bool
  23. SupportsAnimationSave bool
  24. SupportsThumbnail bool
  25. }
  26. // DetectFunc is a function that detects the image type from byte data
  27. type DetectFunc func(r bufreader.ReadPeeker) (Type, error)
  28. // detector is a struct that holds a detection function and its priority
  29. type detector struct {
  30. priority int // priority of the detector, lower is better
  31. fn DetectFunc // function that detects the image type
  32. }
  33. // Registry holds the type registry
  34. type registry struct {
  35. detectors []detector
  36. types []*TypeDesc
  37. typesByName map[string]Type // maps type string to Type
  38. }
  39. // globalRegistry is the default registry instance
  40. var globalRegistry = NewRegistry()
  41. // NewRegistry creates a new image type registry.
  42. func NewRegistry() *registry {
  43. return &registry{
  44. types: nil,
  45. typesByName: make(map[string]Type),
  46. }
  47. }
  48. // RegisterType registers a new image type in the global registry.
  49. // It panics if the type already exists (i.e., if a TypeDesc is already registered for this Type).
  50. func RegisterType(desc *TypeDesc) Type {
  51. return globalRegistry.RegisterType(desc)
  52. }
  53. // GetTypeDesc returns the TypeDesc for the given Type.
  54. // Returns nil if the type is not registered.
  55. func GetTypeDesc(t Type) *TypeDesc {
  56. return globalRegistry.GetTypeDesc(t)
  57. }
  58. // GetTypes returns all registered image types.
  59. func GetTypeByName(name string) (Type, bool) {
  60. return globalRegistry.GetTypeByName(name)
  61. }
  62. // RegisterType registers a new image type in this registry.
  63. // It panics if the type already exists (i.e., if a TypeDesc is already registered for this Type).
  64. func (r *registry) RegisterType(desc *TypeDesc) Type {
  65. r.types = append(r.types, desc)
  66. typ := Type(len(r.types)) // 0 is unknown
  67. r.typesByName[desc.String] = typ
  68. // NOTE: this is a special case for JPEG. The problem is that JPEG is using
  69. // several alternative extensions and processing_options.go is using extension to
  70. // find a type by key. There might be not the only case (e.g. ".tif/.tiff").
  71. // We need to handle this case in a more generic way.
  72. if desc.String == "jpeg" {
  73. // JPEG is a special case, we need to alias it
  74. r.typesByName["jpg"] = typ
  75. }
  76. return typ
  77. }
  78. // GetTypeDesc returns the TypeDesc for the given Type.
  79. // Returns nil if the type is not registered.
  80. func (r *registry) GetTypeDesc(t Type) *TypeDesc {
  81. if t <= 0 { // This would be "default" type
  82. return nil
  83. }
  84. if int(t-1) >= len(r.types) {
  85. return nil
  86. }
  87. return r.types[t-1]
  88. }
  89. // GetTypeByName returns Type by it's name
  90. func (r *registry) GetTypeByName(name string) (Type, bool) {
  91. typ, ok := r.typesByName[name]
  92. return typ, ok
  93. }
  94. // RegisterDetector registers a custom detector function
  95. // Detectors are tried in the order they were registered
  96. func RegisterDetector(priority int, fn DetectFunc) {
  97. globalRegistry.RegisterDetector(priority, fn)
  98. }
  99. // RegisterMagicBytes registers magic bytes for a specific image type
  100. // Magic byte detectors are always tried before custom detectors
  101. func RegisterMagicBytes(typ Type, signature ...[]byte) {
  102. globalRegistry.RegisterMagicBytes(typ, signature...)
  103. }
  104. // Detect attempts to detect the image type from a reader.
  105. // It first tries magic byte detection, then custom detectors in registration order
  106. func Detect(r io.Reader) (Type, error) {
  107. return globalRegistry.Detect(r)
  108. }
  109. // RegisterDetector registers a custom detector function on this registry instance
  110. func (r *registry) RegisterDetector(priority int, fn DetectFunc) {
  111. r.detectors = append(r.detectors, detector{
  112. priority: priority,
  113. fn: fn,
  114. })
  115. // Sort detectors by priority.
  116. // We don't expect a huge number of detectors, and detectors should be registered at startup,
  117. // so sorting them at each registration is okay.
  118. slices.SortStableFunc(r.detectors, func(a, b detector) int {
  119. return a.priority - b.priority
  120. })
  121. }
  122. // RegisterMagicBytes registers magic bytes for a specific image type on this registry instance
  123. func (r *registry) RegisterMagicBytes(typ Type, signature ...[]byte) {
  124. r.RegisterDetector(-1, func(r bufreader.ReadPeeker) (Type, error) {
  125. for _, sig := range signature {
  126. b, err := r.Peek(len(sig))
  127. if err != nil {
  128. return Unknown, err
  129. }
  130. if hasMagicBytes(b, sig) {
  131. return typ, nil
  132. }
  133. }
  134. return Unknown, nil
  135. })
  136. }
  137. // Detect runs image format detection
  138. func (r *registry) Detect(re io.Reader) (Type, error) {
  139. br := bufreader.New(io.LimitReader(re, maxDetectionLimit))
  140. for _, d := range r.detectors {
  141. br.Rewind()
  142. typ, err := d.fn(br)
  143. if err != nil && err != io.EOF && err != io.ErrUnexpectedEOF {
  144. return Unknown, newTypeDetectionError(err)
  145. }
  146. if err == nil && typ != Unknown {
  147. return typ, nil
  148. }
  149. }
  150. return Unknown, newUnknownFormatError()
  151. }
  152. // hasMagicBytes checks if the data matches a magic byte signature
  153. // Supports '?' characters in signature which match any byte
  154. func hasMagicBytes(data []byte, magic []byte) bool {
  155. if len(data) < len(magic) {
  156. return false
  157. }
  158. for i, c := range magic {
  159. if c != data[i] && c != '?' {
  160. return false
  161. }
  162. }
  163. return true
  164. }