| 33 | } |
| 34 | |
| 35 | func resolveSlidesImageSource(localImage, imageURL string) (slidesImageSource, error) { |
| 36 | imageURL = strings.TrimSpace(imageURL) |
| 37 | if localImage == "" && imageURL == "" { |
| 38 | return slidesImageSource{}, usage("required: image argument or --url") |
| 39 | } |
| 40 | if localImage != "" && imageURL != "" { |
| 41 | return slidesImageSource{}, usage("image argument and --url are mutually exclusive") |
| 42 | } |
| 43 | if imageURL != "" { |
| 44 | parsed, err := url.ParseRequestURI(imageURL) |
| 45 | if err != nil || !strings.EqualFold(parsed.Scheme, "https") || parsed.Host == "" || parsed.User != nil { |
| 46 | return slidesImageSource{}, usage("--url must be a public HTTPS image URL without embedded credentials") |
| 47 | } |
| 48 | return slidesImageSource{imageURL: parsed.String()}, nil |
| 49 | } |
| 50 | |
| 51 | ext := strings.ToLower(filepath.Ext(localImage)) |
| 52 | var mimeType string |
| 53 | switch ext { |
| 54 | case extPNG: |
| 55 | mimeType = mimePNG |
| 56 | case imageExtJPG, imageExtJPEG: |
| 57 | mimeType = imageMimeJPEG |
| 58 | case imageExtGIF: |
| 59 | mimeType = imageMimeGIF |
| 60 | default: |
| 61 | return slidesImageSource{}, usagef("unsupported image format %q (use PNG, JPG, or GIF)", ext) |
| 62 | } |
| 63 | return slidesImageSource{localPath: localImage, mimeType: mimeType}, nil |
| 64 | } |
| 65 | |
| 66 | func prepareSlidesImageURL(ctx context.Context, account string, source slidesImageSource) (string, func(), error) { |
| 67 | if source.imageURL != "" { |