package d2svgimport

import (
	
	
	
	
	
	
	
	
	
	

	
	
)

const embeddedRasterAssetPrefix = "svg-raster:"

func ( *svgImporter) ( *svgElement) (svgGeometry, error) {
	if len(.children) != 0 {
		return svgGeometry{}, .errorf("element <image> cannot contain child elements")
	}
	,  := .attrs["href"]
	if ! {
		return svgGeometry{}, .errorf("element <image> requires one data: href")
	}
	if ,  := .attrs["width"]; ! {
		return svgGeometry{}, .errorf("element <image> requires explicit width and height")
	}
	if ,  := .attrs["height"]; ! {
		return svgGeometry{}, .errorf("element <image> requires explicit width and height")
	}

	,  := .lengthAttribute(, "x", 0, false)
	if  != nil {
		return svgGeometry{}, 
	}
	,  := .lengthAttribute(, "y", 0, false)
	if  != nil {
		return svgGeometry{}, 
	}
	,  := .lengthAttribute(, "width", 0, true)
	if  != nil {
		return svgGeometry{}, 
	}
	,  := .lengthAttribute(, "height", 0, true)
	if  != nil {
		return svgGeometry{}, 
	}
	if  <= 0 ||  <= 0 {
		return svgGeometry{}, .errorf("element <image> width and height must be positive")
	}
	if math.IsInf(+, 0) || math.IsNaN(+) || math.IsInf(+, 0) || math.IsNaN(+) {
		return svgGeometry{}, .errorf("element <image> geometry exceeds the finite numeric domain")
	}
	,  := .parseAspectRatioFor(.attrs["preserveAspectRatio"], "embedded image")
	if  != nil {
		return svgGeometry{}, 
	}
	,  := .resolveEmbeddedRaster()
	if  != nil {
		return svgGeometry{}, 
	}
	return svgGeometry{
		kind: geometryImage, box: d2scene.Box{X: , Y: , Width: , Height: },
		asset: , aspect: ,
	}, nil
}

func ( *svgImporter) ( string) (d2scene.AssetID, error) {
	, ,  := .parseEmbeddedRasterDataURI()
	if  != nil {
		return "", 
	}
	,  := decodeCanonicalEmbeddedBase64(.ctx, )
	if  != nil {
		if  := .ctx.Err();  != nil {
			return "", 
		}
		return "", .errorf("element <image> has malformed canonical base64 data")
	}
	if  := .ctx.Err();  != nil {
		return "", 
	}
	if len() == 0 {
		return "", .errorf("element <image> data payload is empty")
	}
	if len() > .limits.MaxBytes {
		return "", .errorf("embedded raster bytes exceed limit %d", .limits.MaxBytes)
	}
	 := embeddedRasterFormatForMIME()
	if  := embeddedRasterSignature();  == "" {
		return "", .errorf("element <image> data has no supported raster signature")
	} else if  !=  {
		return "", .errorf("element <image> MIME type does not match its raster signature")
	}

	,  := .embeddedRasterContentID(, )
	if  != nil {
		return "", 
	}
	if ,  := .assets[];  {
		,  := .(d2scene.RasterAsset)
		if ! || .MIMEType !=  {
			return "", .errorf("embedded raster content ID collision")
		}
		,  := equalRasterBytes(.ctx, .Data, )
		if  != nil {
			return "", 
		}
		if ! {
			return "", .errorf("embedded raster content ID collision")
		}
		return , nil
	}
	if len(.ids)+len(.assets) >= .limits.MaxResources {
		return "", .errorf("declared resource count exceeds limit %d", .limits.MaxResources)
	}
	if len() > .limits.MaxBytes-.rasterBytes {
		return "", .errorf("embedded raster bytes exceed limit %d", .limits.MaxBytes)
	}
	if  := inspectStaticEmbeddedRaster(.ctx, , );  != nil {
		if  := .ctx.Err();  != nil {
			return "", 
		}
		return "", .errorf("element <image> has unsupported or malformed %s data: %v", strings.ToUpper(), )
	}

	, ,  := rasterimage.Config(.ctx, , )
	if  != nil {
		if  := .ctx.Err();  != nil {
			return "", 
		}
		return "", .errorf("element <image> has malformed %s dimensions: %v", strings.ToUpper(), )
	}
	if  {
		return "", .errorf("element <image> has unsupported animated %s data", strings.ToUpper())
	}
	,  := embeddedRasterDecodedBytes()
	if  != nil {
		return "", .errorf("element <image> has invalid decoded dimensions: %v", )
	}
	if  > int64(.limits.MaxBytes)-.rasterDecodedBytes {
		return "", .errorf("embedded raster decoded bytes exceed limit %d", .limits.MaxBytes)
	}

	if  := .ctx.Err();  != nil {
		return "", 
	}

	.assets[] = d2scene.RasterAsset{
		MIMEType: , Data: , PixelWidth: .Width, PixelHeight: .Height, DecodedBytes: ,
	}
	.rasterBytes += len()
	.rasterDecodedBytes += 
	return , nil
}

func decodeCanonicalEmbeddedBase64( context.Context,  string) ([]byte, error) {
	 := 0
	if strings.HasSuffix(, "=") {
		++
	}
	if strings.HasSuffix(, "==") {
		++
	}
	 := base64.StdEncoding.DecodedLen(len()) - 
	if  < 0 {
		return nil, base64.CorruptInputError(0)
	}
	 := make([]byte, )
	const  = 32 << 10 // divisible by one four-byte base64 quantum
	 := 0
	for  := 0;  < len();  +=  {
		if  := .Err();  != nil {
			return nil, 
		}
		 :=  + 
		if  > len() {
			 = len()
		}
		,  := base64.StdEncoding.Strict().Decode([:], []byte([:]))
		if  != nil {
			return nil, 
		}
		 += 
	}
	if  != len() {
		return nil, base64.CorruptInputError(len())
	}
	if  := .Err();  != nil {
		return nil, 
	}
	return , nil
}

func ( *svgImporter) ( string) (,  string,  error) {
	if !strings.HasPrefix(, "data:") {
		return "", "", .errorf("element <image> href must be one embedded data: raster; external, file, network, blob, and local references are forbidden")
	}
	 := [len("data:"):]
	const  = 64
	 := -1
	for  := 0;  < len() &&  <= ; ++ {
		if &31 == 0 {
			if  := .ctx.Err();  != nil {
				return "", "", 
			}
		}
		if [] == ',' {
			 = 
			break
		}
	}
	if  < 0 {
		if len() >  {
			return "", "", .errorf("element <image> data URI metadata exceeds limit %d", )
		}
		return "", "", .errorf("element <image> data URI is missing its comma separator")
	}
	,  := [:], [+1:]
	 := strings.Split(, ";")
	if len() != 2 || [1] != "base64" {
		return "", "", .errorf("element <image> data URI must contain one MIME type followed by one final base64 marker")
	}
	switch [0] {
	case "image/png", "image/jpeg", "image/gif", "image/webp":
		 = [0]
	default:
		return "", "", .errorf("element <image> has unsupported data URI MIME type; expected image/png, image/jpeg, image/gif, or image/webp")
	}
	if  == "" {
		return "", "", .errorf("element <image> data payload is empty")
	}
	if len()%4 != 0 {
		return "", "", .errorf("element <image> has malformed canonical base64 data")
	}
	 := -1
	for  := 0;  < len(); ++ {
		if &4095 == 0 {
			if  := .ctx.Err();  != nil {
				return "", "", 
			}
		}
		 := []
		if  >= 'A' &&  <= 'Z' ||  >= 'a' &&  <= 'z' ||  >= '0' &&  <= '9' ||  == '+' ||  == '/' {
			continue
		}
		if  == '=' &&  >= len()-2 {
			if  < 0 {
				 = 
			}
			continue
		}
		return "", "", .errorf("element <image> has malformed canonical base64 data")
	}
	if  >= 0 {
		 := len() - 
		if  > 2 ||  == 2 && [len()-1] != '=' {
			return "", "", .errorf("element <image> has malformed canonical base64 data")
		}
	}
	return , , .ctx.Err()
}

func ( *svgImporter) ( string,  []byte) (d2scene.AssetID, error) {
	 := sha256.New()
	_, _ = .Write([]byte("d2svgimport:raster\x00"))
	_, _ = .Write([]byte())
	_, _ = .Write([]byte{0})
	const  = 32 << 10
	for  := 0;  < len();  +=  {
		if  := .ctx.Err();  != nil {
			return "", 
		}
		 :=  + 
		if  > len() {
			 = len()
		}
		_, _ = .Write([:])
	}
	if  := .ctx.Err();  != nil {
		return "", 
	}
	return d2scene.AssetID(fmt.Sprintf("%s%x", embeddedRasterAssetPrefix, .Sum(nil))), nil
}

func equalRasterBytes( context.Context, ,  []byte) (bool, error) {
	if len() != len() {
		return false, nil
	}
	const  = 32 << 10
	for  := 0;  < len();  +=  {
		if  := .Err();  != nil {
			return false, 
		}
		 :=  + 
		if  > len() {
			 = len()
		}
		if !bytes.Equal([:], [:]) {
			return false, nil
		}
	}
	return true, .Err()
}

func embeddedRasterFormatForMIME( string) string {
	switch  {
	case "image/png":
		return "png"
	case "image/jpeg":
		return "jpeg"
	case "image/gif":
		return "gif"
	case "image/webp":
		return "webp"
	default:
		return ""
	}
}

func embeddedRasterSignature( []byte) string {
	switch {
	case len() >= 8 && bytes.Equal([:8], []byte("\x89PNG\r\n\x1a\n")):
		return "png"
	case len() >= 3 && [0] == 0xff && [1] == 0xd8 && [2] == 0xff:
		return "jpeg"
	case len() >= 6 && (bytes.Equal([:6], []byte("GIF87a")) || bytes.Equal([:6], []byte("GIF89a"))):
		return "gif"
	case len() >= 12 && bytes.Equal([:4], []byte("RIFF")) && bytes.Equal([8:12], []byte("WEBP")):
		return "webp"
	default:
		return ""
	}
}

func embeddedRasterDecodedBytes( image.Config) (int64, error) {
	if .Width <= 0 || .Height <= 0 {
		return 0, fmt.Errorf("decoded dimensions %dx%d are not positive", .Width, .Height)
	}
	if int64(.Width) > math.MaxInt64/int64(.Height) {
		return 0, fmt.Errorf("decoded pixel count overflows int64")
	}
	 := int64(.Width) * int64(.Height)
	 := int64(4)
	if .ColorModel != nil {
		 := .ColorModel.Convert(color.RGBA64{R: 0x1234, G: 0x5678, B: 0x9abc, A: 0xffff})
		switch .(type) {
		case color.RGBA64, color.NRGBA64, color.Gray16, color.Alpha16:
			 = 8
		}
	}
	if  > math.MaxInt64/ {
		return 0, fmt.Errorf("decoded byte count overflows int64")
	}
	return  * , nil
}

func inspectStaticEmbeddedRaster( context.Context,  []byte,  string) error {
	switch  {
	case "png":
		return inspectEmbeddedPNG(, )
	case "jpeg":
		return inspectEmbeddedJPEG(, )
	case "gif":
		return inspectEmbeddedGIF(, )
	case "webp":
		return inspectEmbeddedWebP(, )
	default:
		return fmt.Errorf("unsupported raster format %q", )
	}
}

func inspectEmbeddedJPEG( context.Context,  []byte) error {
	if len() < 4 || [0] != 0xff || [1] != 0xd8 {
		return fmt.Errorf("JPEG is missing its SOI marker")
	}
	 := 2
	 := false
	for {
		if  := .Err();  != nil {
			return 
		}
		if  {
			for  < len() {
				if &32767 == 0 {
					if  := .Err();  != nil {
						return 
					}
				}
				if [] != 0xff {
					++
					continue
				}
				 := 
				for  < len() && [] == 0xff {
					if &32767 == 0 {
						if  := .Err();  != nil {
							return 
						}
					}
					++
				}
				if  >= len() {
					return fmt.Errorf("JPEG entropy data ends in a partial marker")
				}
				 := []
				if  == 0x00 ||  >= 0xd0 &&  <= 0xd7 {
					++
					continue
				}
				 = 
				 = false
				break
			}
			if  {
				return fmt.Errorf("JPEG is missing its EOI marker")
			}
		}
		if  >= len() || [] != 0xff {
			return fmt.Errorf("JPEG has data outside an entropy-coded scan")
		}
		for  < len() && [] == 0xff {
			if &32767 == 0 {
				if  := .Err();  != nil {
					return 
				}
			}
			++
		}
		if  >= len() {
			return fmt.Errorf("JPEG ends in a partial marker")
		}
		 := []
		++
		switch {
		case  == 0xd9:
			if  != len() {
				return fmt.Errorf("JPEG has trailing data after its EOI marker")
			}
			return nil
		case  == 0xd8:
			return fmt.Errorf("JPEG contains an unexpected nested SOI marker")
		case  == 0x01 ||  >= 0xd0 &&  <= 0xd7:
			continue
		}
		if len()- < 2 {
			return fmt.Errorf("JPEG has a truncated marker length")
		}
		 := int(binary.BigEndian.Uint16([ : +2]))
		if  < 2 ||  > len()- {
			return fmt.Errorf("JPEG has a malformed or truncated marker segment")
		}
		 += 
		if  == 0xda {
			 = true
		}
	}
}

func inspectEmbeddedPNG( context.Context,  []byte) error {
	 := 8
	for {
		if  := .Err();  != nil {
			return 
		}
		if len()- < 12 {
			return fmt.Errorf("truncated PNG chunk")
		}
		 := uint64(binary.BigEndian.Uint32([ : +4]))
		 :=  + 12
		if  > uint64(len()-) {
			return fmt.Errorf("truncated PNG chunk data")
		}
		 := string([+4 : +8])
		if  == "acTL" ||  == "fcTL" ||  == "fdAT" {
			return fmt.Errorf("animated PNG is unsupported")
		}
		 += int()
		if  == "IEND" {
			if  != 0 ||  != len() {
				return fmt.Errorf("PNG has malformed IEND or trailing data")
			}
			return nil
		}
	}
}

func inspectEmbeddedGIF( context.Context,  []byte) error {
	if len() < 13 {
		return fmt.Errorf("truncated GIF logical screen descriptor")
	}
	 := 13
	if [10]&0x80 != 0 {
		 += 3 * (1 << (([10] & 0x07) + 1))
		if  > len() {
			return fmt.Errorf("truncated GIF global color table")
		}
	}
	 := 0
	for {
		if  := .Err();  != nil {
			return 
		}
		if  >= len() {
			return fmt.Errorf("GIF is missing its trailer")
		}
		 := []
		++
		switch  {
		case 0x3b:
			if  != 1 {
				return fmt.Errorf("animated or malformed GIF has %d frames; exactly one is required", )
			}
			if  != len() {
				return fmt.Errorf("GIF has trailing data")
			}
			return nil
		case 0x21:
			if  >= len() {
				return fmt.Errorf("truncated GIF extension")
			}
			++
			var  error
			,  = skipEmbeddedGIFSubBlocks(, , )
			if  != nil {
				return 
			}
		case 0x2c:
			++
			if  > 1 {
				return fmt.Errorf("animated GIF is unsupported")
			}
			if len()- < 9 {
				return fmt.Errorf("truncated GIF image descriptor")
			}
			 := [+8]
			 += 9
			if &0x80 != 0 {
				 += 3 * (1 << (( & 0x07) + 1))
				if  > len() {
					return fmt.Errorf("truncated GIF local color table")
				}
			}
			if  >= len() {
				return fmt.Errorf("GIF is missing its LZW code size")
			}
			++
			var  error
			,  = skipEmbeddedGIFSubBlocks(, , )
			if  != nil {
				return 
			}
		default:
			return fmt.Errorf("unexpected GIF block marker 0x%02x", )
		}
	}
}

func skipEmbeddedGIFSubBlocks( context.Context,  []byte,  int) (int, error) {
	for {
		if  := .Err();  != nil {
			return 0, 
		}
		if  >= len() {
			return 0, fmt.Errorf("truncated GIF sub-blocks")
		}
		 := int([])
		++
		if  == 0 {
			return , nil
		}
		if  > len()- {
			return 0, fmt.Errorf("truncated GIF sub-block data")
		}
		 += 
	}
}

func inspectEmbeddedWebP( context.Context,  []byte) error {
	if len() < 12 {
		return fmt.Errorf("truncated WebP RIFF header")
	}
	 := uint64(binary.LittleEndian.Uint32([4:8])) + 8
	if  != uint64(len()) ||  < 12 {
		return fmt.Errorf("WebP has invalid RIFF length or trailing data")
	}
	 := 0
	for  := 12; uint64() < ; {
		if  := .Err();  != nil {
			return 
		}
		if -uint64() < 8 {
			return fmt.Errorf("truncated WebP chunk header")
		}
		 := uint64(binary.LittleEndian.Uint32([+4 : +8]))
		 :=  + %2
		 := uint64() + 8 + 
		if  >  {
			return fmt.Errorf("truncated WebP chunk data")
		}
		 := string([ : +4])
		if  == "ANIM" ||  == "ANMF" {
			return fmt.Errorf("animated WebP is unsupported")
		}
		if  == "VP8X" &&  >= 1 && [+8]&0x02 != 0 {
			return fmt.Errorf("animated WebP is unsupported")
		}
		if  == "VP8 " ||  == "VP8L" {
			++
			if  > 1 {
				return fmt.Errorf("WebP contains multiple static image bitstreams")
			}
		}
		 = int()
	}
	if  != 1 {
		return fmt.Errorf("WebP must contain exactly one static image bitstream")
	}
	return nil
}