Skip to content

Commit

Permalink
Add support for WebP
Browse files Browse the repository at this point in the history
Closes #24
  • Loading branch information
subdan committed Sep 22, 2020
1 parent eea9c66 commit 474a97c
Show file tree
Hide file tree
Showing 71 changed files with 117 additions and 17 deletions.
9 changes: 7 additions & 2 deletions CONFIG.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,11 @@ android:
# Parameters for exporting images
images:
# Image file format: svg or png
format: png

format: webp
# Format options for webp format only
webpOptions:
# Encoding type: lossy or lossless
encoding: lossy
# Encoding quality in percents. Only for lossy encoding.
quality: 90
```
2 changes: 1 addition & 1 deletion Examples/AndroidExample/.idea/vcs.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Diff not rendered.
Binary file not shown.
Diff not rendered.
Binary file not shown.
Diff not rendered.
Binary file not shown.
Diff not rendered.
Binary file not shown.
Diff not rendered.
Binary file not shown.
9 changes: 8 additions & 1 deletion Examples/AndroidExample/figma-export.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,11 @@ figma:
android:
mainRes: ./app/src/main/res
images:
format: png
# Image file format: svg, png or webp
format: webp
# Format options for webp format only
webpOptions:
# Encoding type: lossy or lossless
encoding: lossy
# Encoding quality in percents. Only for lossy encoding.
quality: 85
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ Colors will be exported to `values/colors.xml` and `values-night/colors.xml` fil
Icons will be exported to `drawable` directory as vector xml files.

Vector images will be exported to `drawable` and `drawable-night` directories as vector `xml` files.
Raster images will be exported to `drawable-???dpi` and `drawable-night-???dpi` directories as `png` files.
Raster images will be exported to `drawable-???dpi` and `drawable-night-???dpi` directories as `png` or `webp` files.

## Installation

Expand All @@ -215,6 +215,8 @@ Raster images will be exported to `drawable-???dpi` and `drawable-night-???dpi`
```
brew install RedMadRobot/formulae/figma-export
```
If you want to export raster images in WebP format install [cwebp](https://developers.google.com/speed/webp/docs/using) command line utility.
brew install webp

### CocoaPods + Fastlane
Add the following line to your Podfile:
Expand Down Expand Up @@ -255,7 +257,7 @@ Run `fastlane sync_colors` to run FigmaExport.

`./figma-export images -i figma-export.yaml`

To export typography use `typography` argument:
To export typography (iOS only) use `typography` argument:

`./figma-export typography -i figma-export.yaml`

Expand Down
10 changes: 10 additions & 0 deletions Sources/FigmaExport/Input/Params.swift
Original file line number Diff line number Diff line change
Expand Up @@ -86,8 +86,18 @@ struct Params: Decodable {
enum Format: String, Decodable {
case svg
case png
case webp
}
struct FormatOptions: Decodable {
enum Encoding: String, Decodable {
case lossy
case lossless
}
let encoding: Encoding
let quality: Int?
}
let format: Format
let webpOptions: FormatOptions?
}
let mainRes: URL
let images: Images
Expand Down
5 changes: 3 additions & 2 deletions Sources/FigmaExport/Loaders/ImagesLoader.swift
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ final class ImagesLoader {
}

func loadImages(filter: String? = nil) throws -> (light: [ImagePack], dark: [ImagePack]?) {
if (platform == .android && params.android?.images.format == .png) || platform == .ios {
switch (platform, params.android?.images.format) {
case (.android, .png), (.android, .webp), (.ios, .none):
let lightImages = try loadPNGImages(
fileId: params.figma.lightFileId,
frameName: .illustrations,
Expand All @@ -52,7 +53,7 @@ final class ImagesLoader {
lightImages,
darkImages
)
} else {
default:
let light = try _loadImages(
fileId: params.figma.lightFileId,
frameName: .illustrations,
Expand Down
32 changes: 32 additions & 0 deletions Sources/FigmaExport/Output/WebpConverter.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import Foundation
import FigmaExportCore

/// PNG to WebP converter
final class WebpConverter {

enum Encoding {
case lossy(quality: Int)
case lossless
}

private let encoding: Encoding

init(encoding: Encoding) {
self.encoding = encoding
}

/// Converts PNG files to WebP
func convert(file url: URL) throws {
let outputURL = url.deletingPathExtension().appendingPathExtension("webp")

let task = Process()
task.executableURL = URL(fileURLWithPath: "/usr/local/bin/cwebp")
if case Encoding.lossless = encoding {
task.arguments = ["-lossless", url.path, "-o", outputURL.path, "-short"]
} else if case Encoding.lossy(let quality) = encoding {
task.arguments = ["-q", String(quality), url.path, "-o", outputURL.path, "-short"]
}
try task.run()
task.waitUntilExit()
}
}
2 changes: 1 addition & 1 deletion Sources/FigmaExport/Subcommands/ExportIcons.swift
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ extension FigmaExportCommand {

// 5. Convert all SVG to XML files
logger.info("Converting SVGs to XMLs...")
try fileConverter.convert(inputDirectoryPath: tempDirectoryURL.path)
try svgFileConverter.convert(inputDirectoryPath: tempDirectoryURL.path)

// Create output directory main/res/drawable/
let outputDirectory = URL(fileURLWithPath: android.mainRes.path)
Expand Down
33 changes: 26 additions & 7 deletions Sources/FigmaExport/Subcommands/ExportImages.swift
Original file line number Diff line number Diff line change
Expand Up @@ -107,10 +107,11 @@ extension FigmaExportCommand {
)
let images = try processor.process(light: imagesTuple.light, dark: imagesTuple.dark).get()

if android.images.format == .svg {
switch android.images.format {
case .svg:
try exportAndroidSVGImages(images: images, params: params, logger: logger)
} else if android.images.format == .png {
try exportAndroidPNGImages(images: images, params: params, logger: logger)
case .png, .webp:
try exportAndroidRasterImages(images: images, params: params, logger: logger)
}

logger.info("Done!")
Expand Down Expand Up @@ -147,10 +148,10 @@ extension FigmaExportCommand {

// Convert all SVG to XML files
logger.info("Converting SVGs to XMLs...")
try fileConverter.convert(inputDirectoryPath: tempDirectoryLightURL.path)
try svgFileConverter.convert(inputDirectoryPath: tempDirectoryLightURL.path)
if images.first?.dark != nil {
logger.info("Converting dark SVGs to XMLs...")
try fileConverter.convert(inputDirectoryPath: tempDirectoryDarkURL.path)
try svgFileConverter.convert(inputDirectoryPath: tempDirectoryDarkURL.path)
}

logger.info("Writting files to Android Studio project...")
Expand Down Expand Up @@ -185,7 +186,7 @@ extension FigmaExportCommand {
try FileManager.default.removeItem(at: tempDirectoryDarkURL)
}

private func exportAndroidPNGImages(images: [AssetPair<ImagesProcessor.AssetType>], params: Params, logger: Logger) throws {
private func exportAndroidRasterImages(images: [AssetPair<ImagesProcessor.AssetType>], params: Params, logger: Logger) throws {
guard let android = params.android else { return }

// Create empty temp directory
Expand All @@ -207,10 +208,28 @@ extension FigmaExportCommand {

// Move downloaded files to new empty temp directory
try fileWritter.write(files: localFiles)

// Convert to WebP
if android.images.format == .webp, let options = android.images.webpOptions {
logger.info("Converting PNG files to WebP...")
let converter: WebpConverter
switch (options.encoding, options.quality) {
case (.lossless, _):
converter = WebpConverter(encoding: .lossless)
case (.lossy, let quality?):
converter = WebpConverter(encoding: .lossy(quality: quality))
case (.lossy, .none):
fatalError("Encoding quality not specified. Set android.images.webpOptions.quality in YAML file.")
}
localFiles = try localFiles.map { file in
try converter.convert(file: file.destination.url)
return file.changingExtension(newExtension: "webp")
}
}

logger.info("Writting files to Android Studio project...")

// Move PNG files to main/res/drawable-XXXdpi/
// Move PNG/WebP files to main/res/drawable-XXXdpi/
localFiles = localFiles.map { fileContents -> FileContents in
let directoryName = Drawable.scaleToDrawableName(fileContents.scale, dark: fileContents.dark)
let directory = URL(fileURLWithPath: android.mainRes.path).appendingPathComponent(directoryName, isDirectory: true)
Expand Down
2 changes: 1 addition & 1 deletion Sources/FigmaExport/main.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ enum FigmaExportError: LocalizedError {

struct FigmaExportCommand: ParsableCommand {

static let fileConverter = VectorDrawableConverter()
static let svgFileConverter = VectorDrawableConverter()
static let fileWritter = FileWritter()
static let fileDownloader = FileDownloader()

Expand Down
24 changes: 24 additions & 0 deletions Sources/FigmaExportCore/FileContents.swift
Original file line number Diff line number Diff line change
Expand Up @@ -55,4 +55,28 @@ public struct FileContents: Equatable {
self.dataFile = dataFile
self.sourceURL = nil
}

/// Make a copy of the FileContents with different file extension
/// - Parameter newExtension: New file extension
public func changingExtension(newExtension: String) -> FileContents {
var newFile: FileContents

let newFileURL = self.destination.file.deletingPathExtension().appendingPathExtension(newExtension)
let newDestination = Destination(directory: self.destination.directory, file: newFileURL)

if let sourceURL = sourceURL { // Remote file
newFile = FileContents(destination: newDestination, sourceURL: sourceURL)
} else if let dataFile = dataFile { // On-disk file
newFile = FileContents(destination: newDestination, dataFile: dataFile)
} else if let data = data { // In-memory file
newFile = FileContents(destination: newDestination, data: data)
} else {
fatalError("Unable to change file extension.")
}

newFile.scale = self.scale
newFile.dark = self.dark

return newFile
}
}

0 comments on commit 474a97c

Please sign in to comment.