mirror of
https://github.com/SimpleMobileTools/Simple-Draw.git
synced 2025-06-05 21:59:17 +02:00
appending .pro to package name
This commit is contained in:
@ -0,0 +1,41 @@
|
||||
package com.simplemobiletools.draw.pro.models
|
||||
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
import android.view.View
|
||||
import java.util.*
|
||||
|
||||
internal class MyParcelable : View.BaseSavedState {
|
||||
var paths = LinkedHashMap<MyPath, PaintOptions>()
|
||||
|
||||
constructor(superState: Parcelable) : super(superState)
|
||||
|
||||
constructor(parcel: Parcel) : super(parcel) {
|
||||
val size = parcel.readInt()
|
||||
for (i in 0 until size) {
|
||||
val key = parcel.readSerializable() as MyPath
|
||||
val paintOptions = PaintOptions(parcel.readInt(), parcel.readFloat(), parcel.readInt() == 1)
|
||||
paths[key] = paintOptions
|
||||
}
|
||||
}
|
||||
|
||||
override fun writeToParcel(out: Parcel, flags: Int) {
|
||||
super.writeToParcel(out, flags)
|
||||
out.writeInt(paths.size)
|
||||
for ((path, paintOptions) in paths) {
|
||||
out.writeSerializable(path)
|
||||
out.writeInt(paintOptions.color)
|
||||
out.writeFloat(paintOptions.strokeWidth)
|
||||
out.writeInt(if (paintOptions.isEraser) 1 else 0)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
@JvmField
|
||||
val CREATOR: Parcelable.Creator<MyParcelable> = object : Parcelable.Creator<MyParcelable> {
|
||||
override fun createFromParcel(source: Parcel) = MyParcelable(source)
|
||||
|
||||
override fun newArray(size: Int) = arrayOf<MyParcelable>()
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,82 @@
|
||||
package com.simplemobiletools.draw.pro.models
|
||||
|
||||
import android.app.Activity
|
||||
import android.graphics.Path
|
||||
import com.simplemobiletools.commons.extensions.toast
|
||||
import com.simplemobiletools.draw.pro.R
|
||||
import com.simplemobiletools.draw.pro.actions.Action
|
||||
import com.simplemobiletools.draw.pro.actions.Line
|
||||
import com.simplemobiletools.draw.pro.actions.Move
|
||||
import com.simplemobiletools.draw.pro.actions.Quad
|
||||
import java.io.ObjectInputStream
|
||||
import java.io.Serializable
|
||||
import java.security.InvalidParameterException
|
||||
import java.util.*
|
||||
|
||||
// https://stackoverflow.com/a/8127953
|
||||
class MyPath : Path(), Serializable {
|
||||
val actions = LinkedList<Action>()
|
||||
|
||||
private fun readObject(inputStream: ObjectInputStream) {
|
||||
inputStream.defaultReadObject()
|
||||
|
||||
val copiedActions = actions.map { it }
|
||||
copiedActions.forEach {
|
||||
it.perform(this)
|
||||
}
|
||||
}
|
||||
|
||||
fun readObject(pathData: String, activity: Activity) {
|
||||
val tokens = pathData.split("\\s+".toRegex()).dropLastWhile(String::isEmpty).toTypedArray()
|
||||
var i = 0
|
||||
try {
|
||||
while (i < tokens.size) {
|
||||
when (tokens[i][0]) {
|
||||
'M' -> addAction(Move(tokens[i]))
|
||||
'L' -> addAction(Line(tokens[i]))
|
||||
'Q' -> {
|
||||
// Quad actions are of the following form:
|
||||
// "Qx1,y1 x2,y2"
|
||||
// Since we split the tokens by whitespace, we need to join them again
|
||||
if (i + 1 >= tokens.size)
|
||||
throw InvalidParameterException("Error parsing the data for a Quad.")
|
||||
|
||||
addAction(Quad(tokens[i] + " " + tokens[i + 1]))
|
||||
++i
|
||||
}
|
||||
}
|
||||
++i
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
activity.toast(R.string.unknown_error_occurred)
|
||||
}
|
||||
}
|
||||
|
||||
override fun reset() {
|
||||
actions.clear()
|
||||
super.reset()
|
||||
}
|
||||
|
||||
private fun addAction(action: Action) {
|
||||
when (action) {
|
||||
is Move -> moveTo(action.x, action.y)
|
||||
is Line -> lineTo(action.x, action.y)
|
||||
is Quad -> quadTo(action.x1, action.y1, action.x2, action.y2)
|
||||
}
|
||||
}
|
||||
|
||||
override fun moveTo(x: Float, y: Float) {
|
||||
actions.add(Move(x, y))
|
||||
super.moveTo(x, y)
|
||||
}
|
||||
|
||||
override fun lineTo(x: Float, y: Float) {
|
||||
actions.add(Line(x, y))
|
||||
super.lineTo(x, y)
|
||||
}
|
||||
|
||||
override fun quadTo(x1: Float, y1: Float, x2: Float, y2: Float) {
|
||||
actions.add(Quad(x1, y1, x2, y2))
|
||||
super.quadTo(x1, y1, x2, y2)
|
||||
}
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
package com.simplemobiletools.draw.pro.models
|
||||
|
||||
import android.graphics.Color
|
||||
|
||||
data class PaintOptions(var color: Int = Color.BLACK, var strokeWidth: Float = 5f, var isEraser: Boolean = false) {
|
||||
fun getColorToExport() = if (isEraser) "none" else "#${Integer.toHexString(color).substring(2)}"
|
||||
}
|
141
app/src/main/kotlin/com/simplemobiletools/draw/pro/models/Svg.kt
Normal file
141
app/src/main/kotlin/com/simplemobiletools/draw/pro/models/Svg.kt
Normal file
@ -0,0 +1,141 @@
|
||||
package com.simplemobiletools.draw.pro.models
|
||||
|
||||
import android.graphics.Color
|
||||
import android.graphics.drawable.ColorDrawable
|
||||
import android.net.Uri
|
||||
import android.sax.RootElement
|
||||
import android.util.Xml
|
||||
import com.simplemobiletools.commons.extensions.getFileOutputStream
|
||||
import com.simplemobiletools.commons.extensions.getFilenameFromPath
|
||||
import com.simplemobiletools.commons.extensions.toast
|
||||
import com.simplemobiletools.commons.models.FileDirItem
|
||||
import com.simplemobiletools.draw.pro.R
|
||||
import com.simplemobiletools.draw.pro.activities.MainActivity
|
||||
import com.simplemobiletools.draw.pro.activities.SimpleActivity
|
||||
import com.simplemobiletools.draw.pro.views.MyCanvas
|
||||
import java.io.*
|
||||
import java.util.*
|
||||
|
||||
object Svg {
|
||||
fun saveSvg(activity: SimpleActivity, path: String, canvas: MyCanvas) {
|
||||
val backgroundColor = (canvas.background as ColorDrawable).color
|
||||
|
||||
activity.getFileOutputStream(FileDirItem(path, path.getFilenameFromPath()), true) {
|
||||
if (it != null) {
|
||||
val writer = BufferedWriter(OutputStreamWriter(it))
|
||||
writeSvg(writer, backgroundColor, canvas.mPaths, canvas.width, canvas.height)
|
||||
writer.close()
|
||||
activity.toast(R.string.file_saved)
|
||||
} else {
|
||||
activity.toast(R.string.unknown_error_occurred)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun writeSvg(writer: Writer, backgroundColor: Int, paths: Map<MyPath, PaintOptions>, width: Int, height: Int) {
|
||||
writer.apply {
|
||||
write("<svg width=\"$width\" height=\"$height\" xmlns=\"http://www.w3.org/2000/svg\">")
|
||||
write("<rect width=\"$width\" height=\"$height\" fill=\"#${Integer.toHexString(backgroundColor).substring(2)}\"/>")
|
||||
|
||||
for ((key, value) in paths) {
|
||||
writePath(this, key, value)
|
||||
}
|
||||
write("</svg>")
|
||||
}
|
||||
}
|
||||
|
||||
private fun writePath(writer: Writer, path: MyPath, options: PaintOptions) {
|
||||
writer.apply {
|
||||
write("<path d=\"")
|
||||
path.actions.forEach {
|
||||
it.perform(this)
|
||||
write(" ")
|
||||
}
|
||||
|
||||
write("\" fill=\"none\" stroke=\"")
|
||||
write(options.getColorToExport())
|
||||
write("\" stroke-width=\"")
|
||||
write(options.strokeWidth.toString())
|
||||
write("\" stroke-linecap=\"round\"/>")
|
||||
}
|
||||
}
|
||||
|
||||
fun loadSvg(activity: MainActivity, fileOrUri: Any, canvas: MyCanvas) {
|
||||
val svg = parseSvg(activity, fileOrUri)
|
||||
|
||||
canvas.clearCanvas()
|
||||
activity.setBackgroundColor(svg.background!!.color)
|
||||
|
||||
svg.paths.forEach {
|
||||
val path = MyPath()
|
||||
path.readObject(it.data, activity)
|
||||
val options = PaintOptions(it.color, it.strokeWidth, it.isEraser)
|
||||
|
||||
canvas.addPath(path, options)
|
||||
}
|
||||
}
|
||||
|
||||
private fun parseSvg(activity: MainActivity, fileOrUri: Any): SSvg {
|
||||
var inputStream: InputStream? = null
|
||||
val svg = SSvg()
|
||||
try {
|
||||
inputStream = when (fileOrUri) {
|
||||
is File -> FileInputStream(fileOrUri)
|
||||
is Uri -> activity.contentResolver.openInputStream(fileOrUri)
|
||||
else -> null
|
||||
}
|
||||
|
||||
// Actual parsing (http://stackoverflow.com/a/4828765)
|
||||
val ns = "http://www.w3.org/2000/svg"
|
||||
val root = RootElement(ns, "svg")
|
||||
val rectElement = root.getChild(ns, "rect")
|
||||
val pathElement = root.getChild(ns, "path")
|
||||
|
||||
root.setStartElementListener { attributes ->
|
||||
val width = attributes.getValue("width").toInt()
|
||||
val height = attributes.getValue("height").toInt()
|
||||
svg.setSize(width, height)
|
||||
}
|
||||
|
||||
rectElement.setStartElementListener { attributes ->
|
||||
val width = attributes.getValue("width").toInt()
|
||||
val height = attributes.getValue("height").toInt()
|
||||
val color = Color.parseColor(attributes.getValue("fill"))
|
||||
if (svg.background != null)
|
||||
throw UnsupportedOperationException("Unsupported SVG, should only have one <rect>.")
|
||||
|
||||
svg.background = SRect(width, height, color)
|
||||
}
|
||||
|
||||
pathElement.setStartElementListener { attributes ->
|
||||
val d = attributes.getValue("d")
|
||||
val width = attributes.getValue("stroke-width").toFloat()
|
||||
val stroke = attributes.getValue("stroke")
|
||||
val isEraser = stroke == "none"
|
||||
val color = if (isEraser) 0 else Color.parseColor(stroke)
|
||||
svg.paths.add(SPath(d, color, width, isEraser))
|
||||
}
|
||||
|
||||
Xml.parse(inputStream, Xml.Encoding.UTF_8, root.contentHandler)
|
||||
} finally {
|
||||
inputStream?.close()
|
||||
}
|
||||
return svg
|
||||
}
|
||||
|
||||
private class SSvg : Serializable {
|
||||
var background: SRect? = null
|
||||
val paths: ArrayList<SPath> = ArrayList()
|
||||
private var width = 0
|
||||
private var height = 0
|
||||
|
||||
internal fun setSize(w: Int, h: Int) {
|
||||
width = w
|
||||
height = h
|
||||
}
|
||||
}
|
||||
|
||||
private class SRect(val width: Int, val height: Int, val color: Int) : Serializable
|
||||
|
||||
private class SPath(var data: String, var color: Int, var strokeWidth: Float, var isEraser: Boolean) : Serializable
|
||||
}
|
Reference in New Issue
Block a user