[go: nahoru, domu]

blob: 7b7acbd21fc05a9cc2a73fbfbf062e9dd65e8682 [file] [log] [blame]
/*
* Copyright 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package androidx.ui.material.icons.generator
import androidx.ui.material.icons.generator.vector.PathParser
import androidx.ui.material.icons.generator.vector.Vector
import androidx.ui.material.icons.generator.vector.VectorNode
import org.xmlpull.v1.XmlPullParser
import org.xmlpull.v1.XmlPullParser.END_DOCUMENT
import org.xmlpull.v1.XmlPullParser.END_TAG
import org.xmlpull.v1.XmlPullParser.START_TAG
import org.xmlpull.v1.XmlPullParserException
import org.xmlpull.v1.XmlPullParserFactory
/**
* Parser that converts [icon]s into [Vector]s
*/
class IconParser(private val icon: Icon) {
/**
* @return a [Vector] representing the provided [icon].
*/
fun parse(): Vector {
val parser = XmlPullParserFactory.newInstance().newPullParser().apply {
setInput(icon.fileContent.byteInputStream(), null)
seekToStartTag()
}
check(parser.name == "vector") { "The start tag must be <vector>!" }
parser.next()
val nodes = mutableListOf<VectorNode>()
var currentGroup: VectorNode.Group? = null
while (!parser.isAtEnd()) {
when (parser.eventType) {
START_TAG -> {
when (parser.name) {
PATH -> {
val pathData = parser.getAttributeValue(
null,
PATH_DATA
)
val fillAlpha = parser.getValueAsFloat(FILL_ALPHA)
val strokeAlpha = parser.getValueAsFloat(STROKE_ALPHA)
val path =
VectorNode.Path(
strokeAlpha = strokeAlpha ?: 1f,
fillAlpha = fillAlpha ?: 1f,
nodes = PathParser.parsePathString(pathData)
)
if (currentGroup != null) {
currentGroup.paths.add(path)
} else {
nodes.add(path)
}
}
// Material icons are simple and don't have nested groups, so this can be simple
GROUP -> {
val group = VectorNode.Group()
currentGroup = group
nodes.add(group)
}
CLIP_PATH -> { /* TODO: b/147418351 - parse clipping paths */
}
}
}
}
parser.next()
}
return Vector(nodes)
}
}
/**
* @return the float value for the attribute [name], or null if it couldn't be found
*/
private fun XmlPullParser.getValueAsFloat(name: String) =
getAttributeValue(null, name)?.toFloatOrNull()
private fun XmlPullParser.seekToStartTag(): XmlPullParser {
var type = next()
while (type != START_TAG && type != END_DOCUMENT) {
// Empty loop
type = next()
}
if (type != START_TAG) {
throw XmlPullParserException("No start tag found")
}
return this
}
private fun XmlPullParser.isAtEnd() =
eventType == END_DOCUMENT || (depth < 1 && eventType == END_TAG)
// XML tag names
private const val CLIP_PATH = "clip-path"
private const val GROUP = "group"
private const val PATH = "path"
// XML attribute names
private const val PATH_DATA = "android:pathData"
private const val FILL_ALPHA = "android:fillAlpha"
private const val STROKE_ALPHA = "android:strokeAlpha"