All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.github.ackeecz.resizin.sdk.Resizin.kt Maven / Gradle / Ivy

There is a newer version: 2.1.0
Show newest version
package io.github.ackeecz.resizin.sdk

import androidx.annotation.VisibleForTesting
import java.util.Locale

/**
 * Resizin.com server instance with variable app id.
 * Provides UrlGenerator for building image url with various modificators
 * like width, height, crop and gravity
 *
 * @param appId id of your app at the Resizin server
 */
class Resizin(private val appId: String) {

    companion object {

        private const val IMAGE_SERVER_URL = "https://img.resizin.com/"
    }

    /**
     * Generate url with previously parsed imageId
     *
     * @return built url as String
     */
    fun url(): UrlGenerator {
        return UrlGenerator(this.appId, IMAGE_SERVER_URL)
    }

    /**
     * Parse given url and parse it to instance
     *
     * @param url URL of existing image
     * @return builder class for creating url
     */
    fun withUrl(url: String): UrlGenerator {
        return UrlGenerator(this.appId, IMAGE_SERVER_URL).parseUrl(url)
    }

    /**
     * Generator of url to image server
     */
    class UrlGenerator internal constructor(private var appId: String, private val serverUrl: String) {

        private var width: Int = 0
        private var height: Int = 0
        private var grayscale: Boolean = false
        private var crop: Crop
        private var gravity: Gravity
        private var widthSet: Boolean = false
        private var heightSet: Boolean = false
        private var cropSet: Boolean = false
        private var gravitySet: Boolean = false
        private var quality: Int = 0
        private var extension: String? = null
        private var upscale = -1
        private var borderTop = -1
        private var borderLeft = -1
        private var borderRight = -1
        private var borderBottom = -1
        var background: String? = null
            @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) get
            private set
        var imageId: String? = null
            internal set

        init {
            width = Integer.MIN_VALUE
            height = Integer.MIN_VALUE
            grayscale = false
            crop = Crop.FIT
            gravity = Gravity.CENTER
            quality = -1
            extension = ""
            imageId = null
        }

        /**
         * Specify width of image
         *
         * @param width of image
         * @return UrlGenerator instance
         */
        fun width(width: Int): UrlGenerator {
            this.widthSet = true
            this.width = width
            return this
        }

        /**
         * Specify height of image
         *
         * @param height of image
         * @return UrlGenerator instance
         */
        fun height(height: Int): UrlGenerator {
            this.heightSet = true
            this.height = height
            return this
        }

        /**
         * Specify if grayscale filter should be applied to image
         *
         * @param grayscale switch for grayscale filtrer
         * @return UrlGenerator instance
         */
        fun grayscale(grayscale: Boolean): UrlGenerator {
            this.grayscale = grayscale
            return this
        }

        /**
         * Specify crop of image. see [Crop] for available
         * options
         *
         * @param crop of image
         * @return UrlGenerator instance
         */
        fun crop(crop: Crop): UrlGenerator {
            this.cropSet = true
            this.crop = crop
            return this
        }

        /**
         * Specify gravity of cropped image. See [Gravity] for
         * available options
         *
         * @param gravity of cropped image
         * @return UrlGenerator instance
         */
        fun gravity(gravity: Gravity): UrlGenerator {
            this.gravitySet = true
            this.gravity = gravity
            return this
        }

        /**
         * Specify quality of image in percent
         *
         * @param quality percentual quality of image, values 0 - 100
         * @return UrlGenerator instance
         */
        fun quality(quality: Int): UrlGenerator {
            this.quality = quality
            return this
        }

        /**
         * Specify extension of image
         *
         * @param extension extension eg. jpg
         * @return UrlGenerator instance
         */
        fun extension(extension: String): UrlGenerator {
            this.extension = extension
            return this
        }

        /**
         * Indicator if image should be upscaled if size is larger than original dimensions
         *
         * @param upscale boolean indicator
         * @return UrlGenerator instance
         */
        fun upscale(upscale: Boolean): UrlGenerator {
            this.upscale = if (upscale) 1 else 0
            return this
        }

        /**
         * Specify additional border of the image
         *
         * @param border width of the border (must be greater or equal to 0)
         * @return UrlGenerator instance
         */
        fun border(border: Int): UrlGenerator {
            this.borderBottom = border
            this.borderRight = this.borderBottom
            this.borderLeft = this.borderRight
            this.borderTop = this.borderLeft
            return this
        }

        /**
         * Specify additional individual borders of the image
         *
         * @param borderTop    width of the top border (must be greater or equal to 0)
         * @param borderLeft   width of the top border (must be greater or equal to 0)
         * @param borderRight  width of the top border (must be greater or equal to 0)
         * @param borderBottom width of the top border (must be greater or equal to 0)
         * @return UrlGenerator instance
         */
        fun border(borderTop: Int, borderLeft: Int, borderRight: Int, borderBottom: Int): UrlGenerator {
            this.borderTop = borderTop
            this.borderLeft = borderLeft
            this.borderRight = borderRight
            this.borderBottom = borderBottom
            return this
        }

        /**
         * Specify background of the image when cropping to fit or using borders
         *
         * @param background color of the background (i.e. "00bcd4" or "#00bcd4")
         * @return UrlGenerator instance
         */
        fun background(background: String?): UrlGenerator {
            if (background != null) {
                if (background.matches("#?[0-9a-fA-F]{6}".toRegex())) {
                    this.background = background.replace("#", "")
                } else {
                    throw IllegalArgumentException("Wrong format of color, use format 'RRGGBB' or '#RRGGBB' (i.e. '#00bcd4')")
                }
            } else {
                this.background = null
            }
            return this
        }

        /**
         * Generates url with specified parameters
         *
         * @param imageId id of image on image server
         * @return built url as String
         */
        @JvmOverloads
        fun generate(imageId: String? = this.imageId): String {
            return buildString {
                append(serverUrl)
                if (!serverUrl.endsWith("/")) {
                    append("/")
                }
                append(appId).append("/")
                append("image").append("/")
                val transformations = createTransformations()
                append(transformations.filter { it.isNotEmpty() }.joinToString("-"))
                if (transformations.isNotEmpty()) {
                    append("/")
                }
                append(imageId)
                if (!extension.isNullOrEmpty()) {
                    append(".").append(extension)
                }
            }
        }

        private fun createTransformations(): List {
            val transformations = mutableListOf()

            if (this.widthSet) {
                transformations.add(String.format(Locale.US, "w_%d", this.width))
            }

            if (this.heightSet) {
                transformations.add(String.format(Locale.US, "h_%d", this.height))
            }

            if (this.cropSet) {
                transformations.add(String.format(Locale.US, "c_%s", this.crop))
            }

            if (this.gravitySet) {
                transformations.add(String.format(Locale.US, "g_%s", this.gravity))
            }

            if (this.grayscale) {
                transformations.add("f_greyscale")
            }

            if (quality >= 0) {
                transformations.add(String.format(Locale.US, "q_%d", this.quality))
            }

            if (upscale >= 0) {
                transformations.add(String.format(Locale.US, "u_%d", this.upscale))
            }

            if (borderTop >= 0 && borderLeft >= 0 && borderRight >= 0 && borderBottom >= 0) {
                transformations.add(String.format(Locale.US, "b_%d_%d_%d_%d", borderTop, borderLeft, borderRight, borderBottom))
            }

            if (background != null) {
                transformations.add(String.format(Locale.US, "bg_%s", background))
            }

            return transformations
        }

        internal fun parseUrl(url: String): UrlGenerator {
            try {
                val parts = url.split("/".toRegex()).dropLastWhile({ it.isEmpty() }).toTypedArray()
                val imageId = parts[parts.size - 1]
                if (parts[4] != "image") {
                    throw Exception()
                }
                if (imageId.contains(".")) {
                    this.imageId = imageId.split("\\.".toRegex()).dropLastWhile({ it.isEmpty() }).toTypedArray()[0]
                    this.extension = imageId.split("\\.".toRegex()).dropLastWhile({ it.isEmpty() }).toTypedArray()[1]
                } else {
                    this.imageId = imageId
                }

                val modificatorsText = parts[parts.size - 2]
                val modificators = modificatorsText.split("-".toRegex()).dropLastWhile({ it.isEmpty() }).toTypedArray()
                for (modificator in modificators) {
                    if (modificator.startsWith("w")) {
                        this.width = Integer.parseInt(modificator.substring(2))
                        widthSet = true
                    }
                    if (modificator.startsWith("h")) {
                        this.height = Integer.parseInt(modificator.substring(2))
                        heightSet = true
                    }
                    if (modificator.startsWith("c")) {
                        cropSet = true
                        crop = Crop.fromValue(modificator.substring(2))
                    }

                    if (modificator.startsWith("g")) {
                        gravitySet = true
                        gravity = Gravity.fromValue(modificator.substring(2))
                    }

                    if (modificator.startsWith("f")) {
                        grayscale = true
                    }

                    if (modificator.startsWith("q")) {
                        quality = Integer.parseInt(modificator.substring(2))
                    }

                    if (modificator.startsWith("u")) {
                        this.upscale = Integer.parseInt(modificator.substring(2))
                    }

                    if (modificator.startsWith("b_")) {
                        val numbersString = modificator.substring(2)
                        val numbers = numbersString.split("_".toRegex()).dropLastWhile({ it.isEmpty() }).toTypedArray()
                        borderTop = Integer.parseInt(numbers[0])
                        borderLeft = Integer.parseInt(numbers[1])
                        borderRight = Integer.parseInt(numbers[2])
                        borderBottom = Integer.parseInt(numbers[3])
                    }
                    if (modificator.startsWith("bg")) {
                        this.background = modificator.substring(3)
                    }
                }
                return this
            } catch (e: Exception) {
                throw InvalidUrlException("Url $url is in incorrect format", e)
            }
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy