Skip to content

Instantly share code, notes, and snippets.

@matisiekpl
Created July 1, 2023 20:13
Show Gist options
  • Save matisiekpl/b660f017f549e110a5f03a8b916ac54d to your computer and use it in GitHub Desktop.
Save matisiekpl/b660f017f549e110a5f03a8b916ac54d to your computer and use it in GitHub Desktop.
package main
import (
"encoding/base64"
"fmt"
"github.com/matisiekpl/figma"
"image"
"image/draw"
)
func RenderRemote(mainImage draw.Image, frame figma.Node, node figma.Node, dry bool, images map[string][]byte) {
if dry {
images[node.ID] = nil
return
}
relativeX, relativeY := GetRelativePosition(frame, node)
html := fmt.Sprintf("<html><body><div><img src='data:image/png;base64,%s'></div></body></html>", base64.StdEncoding.EncodeToString(images[node.ID]))
html += fmt.Sprintf("<style> div { position: relative; width: %fpx; height: %fpx; } </style>", frame.AbsoluteBoundingBox.Width, frame.AbsoluteBoundingBox.Height)
html += fmt.Sprintf("<style> img { position: absolute; left: %fpx; top: %fpx; } </style>", relativeX, relativeY)
html = AddStyling(html, node, "img")
layer := RenderHTML(html, frame.ID, false)
draw.Draw(mainImage, layer.Bounds(), layer, image.Point{}, draw.Over)
}
func RenderRecursively(context RenderContext, mainImage draw.Image, frame figma.Node, node figma.Node, dry bool, images map[string][]byte) {
if CheckIfNodeHasDynamicElements(node) {
if CheckIfNodeIsDynamic(node) {
if !dry {
if node.Type == figma.NodeTypeText {
label := node.Characters
label = "Dark mode"
drawing := RenderText(node, frame, label)
draw.Draw(mainImage, drawing.Bounds(), drawing, image.Point{}, draw.Over)
}
}
} else {
for _, child := range node.Children {
RenderRecursively(context, mainImage, frame, child, dry, images)
}
}
} else {
RenderRemote(mainImage, frame, node, dry, images)
}
}
func RenderFrame(context RenderContext, frame figma.Node) (image.Image, error) {
InitChrome(frame)
mainImage := image.NewRGBA(image.Rect(0, 0, int(frame.AbsoluteBoundingBox.Width), int(frame.AbsoluteBoundingBox.Height)))
html := fmt.Sprintf("<style> body { background-color: rgba(%f,%f,%f,%f); } </style>", frame.BackgroundColor.Red*255, frame.BackgroundColor.Green*255, frame.BackgroundColor.Blue*255, frame.BackgroundColor.Alpha*255)
back := RenderHTML(html, frame.ID, false)
draw.Draw(mainImage, back.Bounds(), back, image.Point{}, draw.Over)
var images = make(map[string][]byte)
RenderRecursively(context, mainImage, frame, frame, true, images)
var ids []string
for k := range images {
ids = append(ids, k)
}
images, err := SendRenderRequest(context, ids)
if err != nil {
return nil, err
}
RenderRecursively(context, mainImage, frame, frame, false, images)
return mainImage, nil
}
func RenderText(node figma.Node, frame figma.Node, label string) image.Image {
relativeX, relativeY := GetRelativePosition(frame, node)
html := fmt.Sprintf("<html><head><link rel='stylesheet' href='https://fonts.googleapis.com/css?family=%s'></head><body>", node.Style.FontFamily)
html += fmt.Sprintf("<style> div { width: %fpx; } </style>", node.AbsoluteBoundingBox.Width)
html += fmt.Sprintf("<style> header { height: %fpx; } </style>", node.AbsoluteBoundingBox.Height)
html += fmt.Sprintf("<style> div { font-size: %fpx; } </style>", node.Style.FontSize)
html += fmt.Sprintf("<style> div { font-weight: %f } </style>", node.Style.FontWeight)
html += fmt.Sprintf("<style> div { color: rgba(%f,%f,%f,%f) } </style>", node.Fills[0].Color.Red*255, node.Fills[0].Color.Green*255, node.Fills[0].Color.Blue*255, node.Fills[0].Color.Alpha*255)
if node.Style.Italic {
html += "<style> div { font-style: italic; } </style>"
}
if node.Style.TextAlignHorizontal == "LEFT" {
html += "<style> div { text-align: left;} </style>"
}
if node.Style.TextAlignHorizontal == "RIGHT" {
html += "<style> div { text-align: right; } </style>"
}
if node.Style.TextAlignHorizontal == "CENTER" {
html += "<style> div { text-align: center; } </style>"
}
if node.Style.TextAlignVertical == "BOTTOM" {
html += "<style> div { position: absolute; bottom: 0; } </style>"
html += "<style> header { position: relative; } </style>"
}
if node.Style.TextAlignVertical == "CENTER" {
html += "<style> div { position: absolute; top: 50%; transform: translateY(-50%); } </style>"
html += "<style> header { position: relative; } </style>"
}
html += "<style> frame { position: relative; width: 100%; height: 100% } </style>"
html += fmt.Sprintf("<style> component { position: absolute; left: %fpx; top: %fpx; } </style>", relativeX, relativeY)
html += fmt.Sprintf("<style> body { font-family: '%s', serif; } </style>", node.Style.FontFamily)
html += AddStyling(html, node, "div")
html += fmt.Sprintf("<frame><component><header><div>%s</div></header></component></frame>", label)
html += "</body></html>"
return RenderHTML(html, frame.ID, true)
}
type RenderContext struct {
Token string
FileKey string
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment