Skip to content

Commit

Permalink
Support exporting typography for Android (#82)
Browse files Browse the repository at this point in the history
  • Loading branch information
simonlee2 authored Mar 22, 2021
1 parent 2273619 commit fc8a900
Show file tree
Hide file tree
Showing 15 changed files with 211 additions and 1 deletion.
4 changes: 4 additions & 0 deletions CONFIG.md
Original file line number Diff line number Diff line change
Expand Up @@ -141,4 +141,8 @@ android:
encoding: lossy
# Encoding quality in percents. Only for lossy encoding.
quality: 90
# Parameters for exporting typography
typography:
# Typography name style: camelCase or snake_case
nameStyle: camelCase
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.redmadrobot.androidexample.ui

import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import com.redmadrobot.androidexample.R

class TypographyFragment: Fragment() {

override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_typography, container, false)
}
}
Binary file not shown.
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/ConstraintLayout"
android:layout_width="match_parent"
android:layout_height="match_parent">

<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:orientation="vertical"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">

<TextView
android:id="@+id/textViewTitle"
style="@style/largeTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/label_large_title" />

<TextView
android:id="@+id/textViewHeader"
style="@style/header"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/label_header" />

<TextView
android:id="@+id/textViewBody"
style="@style/body"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/label_body" />

<TextView
android:id="@+id/textViewCaption"
style="@style/caption"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/label_caption" />

</LinearLayout>

</androidx.constraintlayout.widget.ConstraintLayout>
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,9 @@
android:icon="@drawable/ic_notifications_black_24dp"
android:title="@string/title_icons" />

<item
android:id="@+id/navigation_typography"
android:icon="@drawable/ic_notifications_black_24dp"
android:title="@string/title_typography" />

</menu>
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,10 @@
android:name="com.redmadrobot.androidexample.ui.IconsFragment"
android:label="@string/title_icons"
tools:layout="@layout/fragment_icons" />

<fragment
android:id="@+id/navigation_typography"
android:name="com.redmadrobot.androidexample.ui.TypographyFragment"
android:label="@string/title_typography"
tools:layout="@layout/fragment_typography" />
</navigation>
5 changes: 5 additions & 0 deletions Examples/AndroidExample/app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,9 @@
<string name="title_colors">Colors</string>
<string name="title_images">Images</string>
<string name="title_icons">Icons</string>
<string name="title_typography">Typography</string>
<string name="label_body">Body</string>
<string name="label_caption">Caption</string>
<string name="label_header">Header</string>
<string name="label_large_title">Large Title</string>
</resources>
19 changes: 19 additions & 0 deletions Examples/AndroidExample/app/src/main/res/values/typography.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="body">
<item name="android:fontFamily">@font/ptsans_regular</item>
<item name="android:textSize">16.0sp</item>
</style>
<style name="caption">
<item name="android:fontFamily">@font/ptsans_regular</item>
<item name="android:textSize">14.0sp</item>
</style>
<style name="header">
<item name="android:fontFamily">@font/ptsans_bold</item>
<item name="android:textSize">20.0sp</item>
</style>
<style name="largeTitle">
<item name="android:fontFamily">@font/ptsans_bold</item>
<item name="android:textSize">34.0sp</item>
</style>
</resources>
4 changes: 4 additions & 0 deletions Examples/AndroidExample/figma-export.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,7 @@ android:
encoding: lossy
# Encoding quality in percents. Only for lossy encoding.
quality: 85
# Parameters for exporting typography
typography:
# Typography name style: camelCase or snake_case
nameStyle: camelCase
1 change: 1 addition & 0 deletions Examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,4 @@ There is an example Android Studio project in `AndroidExample` directory which d
* To export colors run: `figma-export colors`
* To export icons run: `figma-export icons`
* To export images run: `figma-export images`
* To export typography run: `figma-export typography`
15 changes: 14 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,8 @@ 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` or `webp` files.

Typography will be exported to `values/typography.xml`

## Installation

Before installation you must provide Figma personal access token via environment variables.
Expand Down Expand Up @@ -266,7 +268,7 @@ Run `fastlane sync_colors` to run FigmaExport.

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

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

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

Expand Down Expand Up @@ -339,11 +341,22 @@ It will generate config file in the current directory.

### Exporting Typography

#### iOS
1. Add a custom font to the Xcode project. Drag & drop font file to the Xcode project, set target membership, and add font file name in the Info.plist file. [See developer documentation for more info.](https://developer.apple.com/documentation/uikit/text_display_and_fonts/adding_a_custom_font_to_your_app)<br><img src="images/fonts.png" width="400" />
2. Run `figma-export typography` to export text styles
3. Add generated Swift files to your Xcode project. FigmaExport doesn’t add swift files to `.xcodeproj` file.
4. Use generated fonts and labels in your code. E.g. `button.titleLabel?.font = UIFont.body()`, `let label = HeaderLabel()`.

### Android
1. Place font file under the `res` directory of your module
2. Run `figma-export typography` to export text styles
3. Create a top level style as a parent for the generated styles. For example:
```
<style name="FigmaExport.TextAppearance" parent="Widget.AppCompat">
</style>
```
4. Use the generated styles in your code

## Design requirements

If a color, icon or image is unique for iOS or Android platform, it should contains "ios" or "android" word in the description field in the properties. If a color, icon or image is used only by the designer and it should not be exported, the word "none" should be specified in the description field.
Expand Down
57 changes: 57 additions & 0 deletions Sources/AndroidExport/AndroidTypographyExporter.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import Foundation
import FigmaExportCore

final public class AndroidTypographyExporter {

private let outputDirectory: URL

public init(outputDirectory: URL) {
self.outputDirectory = outputDirectory
}

public func exportFonts(textStyles: [TextStyle]) throws -> [FileContents] {
[makeFontsFile(textStyles: textStyles)]
}

private func makeFontsFile(textStyles: [TextStyle]) -> FileContents {
let contents = prepareFontsDotXMLContents(textStyles)
let directoryURL = outputDirectory.appendingPathComponent("values")
let fileURL = URL(string: "typography.xml")!

return FileContents(
destination: Destination(directory: directoryURL, file: fileURL),
data: contents
)
}

private func prepareFontsDotXMLContents(_ textStyles: [TextStyle]) -> Data {
let resources = XMLElement(name: "resources")
let xml = XMLDocument(rootElement: resources)
xml.version = "1.0"
xml.characterEncoding = "utf-8"

textStyles.forEach { textStyle in
let textStyleNode = XMLElement(name: "style")
textStyleNode.addAttribute(XMLNode.attribute(withName: "name", stringValue: textStyle.name) as! XMLNode)
resources.addChild(textStyleNode)

let fontFamilyItem = XMLElement(name: "item", stringValue: androidFontName(from: textStyle.fontName))
fontFamilyItem.addAttribute(XMLNode.attribute(withName: "name", stringValue: "android:fontFamily") as! XMLNode)
textStyleNode.addChild(fontFamilyItem)

let fontSizeItem = XMLElement(name: "item", stringValue: androidFontSize(from: textStyle.fontSize))
fontSizeItem.addAttribute(XMLNode.attribute(withName: "name", stringValue: "android:textSize") as! XMLNode)
textStyleNode.addChild(fontSizeItem)
}

return xml.xmlData(options: .nodePrettyPrint)
}

private func androidFontName(from postscriptName: String) -> String {
"@font/\(postscriptName.lowercased().replacingOccurrences(of: "-", with: "_"))"
}

private func androidFontSize(from points: Double) -> String {
"\(points)sp"
}
}
4 changes: 4 additions & 0 deletions Sources/FigmaExport/Input/Params.swift
Original file line number Diff line number Diff line change
Expand Up @@ -121,9 +121,13 @@ struct Params: Decodable {
let format: Format
let webpOptions: FormatOptions?
}
struct Typography: Decodable {
let nameStyle: NameStyle
}
let mainRes: URL
let icons: Icons?
let images: Images?
let typography: Typography?
}

let figma: Figma
Expand Down
25 changes: 25 additions & 0 deletions Sources/FigmaExport/Subcommands/ExportTypography.swift
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,20 @@ extension FigmaExportCommand {
try exportXcodeTextStyles(textStyles: processedTextStyles, iosParams: ios, logger: logger)
logger.info("Done!")
}

if let android = options.params.android {
logger.info("Processing typography...")
let processor = TypographyProcessor(
platform: .android,
nameValidateRegexp: options.params.common?.typography?.nameValidateRegexp,
nameReplaceRegexp: options.params.common?.typography?.nameReplaceRegexp,
nameStyle: options.params.android?.typography?.nameStyle
)
let processedTextStyles = try processor.process(assets: textStyles).get()
logger.info("Saving text styles...")
try exportAndroidTextStyles(textStyles: processedTextStyles, androidParams: android, logger: logger)
logger.info("Done!")
}
}

private func exportXcodeTextStyles(textStyles: [TextStyle], iosParams: Params.iOS, logger: Logger) throws {
Expand Down Expand Up @@ -87,5 +101,16 @@ extension FigmaExportCommand {
logger.error("Unable to add some file references to Xcode project")
}
}

private func exportAndroidTextStyles(textStyles: [TextStyle], androidParams: Params.Android, logger: Logger) throws {

let exporter = AndroidTypographyExporter(outputDirectory: androidParams.mainRes)
let files = try exporter.exportFonts(textStyles: textStyles)

let fileURL = androidParams.mainRes.appendingPathComponent("values/typography.xml")

try? FileManager.default.removeItem(atPath: fileURL.path)
try fileWritter.write(files: files)
}
}
}

0 comments on commit fc8a900

Please sign in to comment.