srcset.go 2.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081
  1. // SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved.
  2. // SPDX-License-Identifier: Apache-2.0
  3. package sanitizer
  4. import (
  5. "fmt"
  6. "strconv"
  7. "strings"
  8. )
  9. type ImageCandidate struct {
  10. ImageURL string
  11. Descriptor string
  12. }
  13. type ImageCandidates []*ImageCandidate
  14. func (c ImageCandidates) String() string {
  15. var htmlCandidates []string
  16. for _, imageCandidate := range c {
  17. var htmlCandidate string
  18. if imageCandidate.Descriptor != "" {
  19. htmlCandidate = fmt.Sprintf(`%s %s`, imageCandidate.ImageURL, imageCandidate.Descriptor)
  20. } else {
  21. htmlCandidate = imageCandidate.ImageURL
  22. }
  23. htmlCandidates = append(htmlCandidates, htmlCandidate)
  24. }
  25. return strings.Join(htmlCandidates, ", ")
  26. }
  27. // ParseSrcSetAttribute returns the list of image candidates from the set.
  28. // https://html.spec.whatwg.org/#parse-a-srcset-attribute
  29. func ParseSrcSetAttribute(attributeValue string) (imageCandidates ImageCandidates) {
  30. unparsedCandidates := strings.Split(attributeValue, ", ")
  31. for _, unparsedCandidate := range unparsedCandidates {
  32. if candidate, err := parseImageCandidate(unparsedCandidate); err == nil {
  33. imageCandidates = append(imageCandidates, candidate)
  34. }
  35. }
  36. return imageCandidates
  37. }
  38. func parseImageCandidate(input string) (*ImageCandidate, error) {
  39. input = strings.TrimSpace(input)
  40. parts := strings.Split(strings.TrimSpace(input), " ")
  41. nbParts := len(parts)
  42. if nbParts > 2 || nbParts == 0 {
  43. return nil, fmt.Errorf(`srcset: invalid number of descriptors`)
  44. }
  45. if nbParts == 2 {
  46. if !isValidWidthOrDensityDescriptor(parts[1]) {
  47. return nil, fmt.Errorf(`srcset: invalid descriptor`)
  48. }
  49. return &ImageCandidate{ImageURL: parts[0], Descriptor: parts[1]}, nil
  50. }
  51. return &ImageCandidate{ImageURL: parts[0]}, nil
  52. }
  53. func isValidWidthOrDensityDescriptor(value string) bool {
  54. if value == "" {
  55. return false
  56. }
  57. lastChar := value[len(value)-1:]
  58. if lastChar != "w" && lastChar != "x" {
  59. return false
  60. }
  61. _, err := strconv.ParseFloat(value[0:len(value)-1], 32)
  62. return err == nil
  63. }