diff --git a/lib/convert-easyeda-json-to-tscircuit-soup-json.ts b/lib/convert-easyeda-json-to-tscircuit-soup-json.ts
index 0d8acfa..e76d069 100644
--- a/lib/convert-easyeda-json-to-tscircuit-soup-json.ts
+++ b/lib/convert-easyeda-json-to-tscircuit-soup-json.ts
@@ -292,12 +292,32 @@ export const convertEasyEdaJsonToCircuitJson = (
soupElements.push(handleHole(h, index))
})
- // Add silkscreen paths and arcs
+ // Add silkscreen paths, arcs and text
easyEdaJson.packageDetail.dataStr.shape.forEach((shape, index) => {
if (shape.type === "TRACK") {
soupElements.push(handleSilkscreenPath(shape, index))
} else if (shape.type === "ARC") {
soupElements.push(handleSilkscreenArc(shape, index))
+ } else if (shape.type === "TEXT") {
+ soupElements.push(
+ Soup.pcb_silkscreen_text.parse({
+ type: "pcb_silkscreen_text",
+ pcb_silkscreen_text_id: `pcb_silkscreen_text_${index + 1}`,
+ pcb_component_id: "pcb_component_1",
+ text: shape.text,
+ anchor_position: {
+ x: mil2mm(shape.x),
+ y: mil2mm(shape.y),
+ },
+ anchor_alignment: {
+ L: "bottom_left",
+ C: "center",
+ R: "bottom_right",
+ }[shape.textAnchor ?? "L"],
+ font_size: shape.size_mm ? shape.size_mm : undefined,
+ layer: "top",
+ } as Soup.PcbSilkscreenTextInput),
+ )
}
})
diff --git a/lib/generate-footprint-tsx.ts b/lib/generate-footprint-tsx.ts
index 75267d4..856a655 100644
--- a/lib/generate-footprint-tsx.ts
+++ b/lib/generate-footprint-tsx.ts
@@ -17,6 +17,7 @@ export const generateFootprintTsx = (
const platedHoles = su(circuitJson).pcb_plated_hole.list()
const smtPads = su(circuitJson).pcb_smtpad.list()
const silkscreenPaths = su(circuitJson).pcb_silkscreen_path.list()
+ const silkscreenTexts = su(circuitJson).pcb_silkscreen_text.list()
const elementStrings: string[] = []
@@ -60,6 +61,12 @@ export const generateFootprintTsx = (
)
}
+ for (const silkscreenText of silkscreenTexts) {
+ elementStrings.push(
+ ``,
+ )
+ }
+
return `
${elementStrings.join("\n")}
diff --git a/lib/schemas/package-detail-shape-schema.ts b/lib/schemas/package-detail-shape-schema.ts
index 3a70fb2..f1cb1e9 100644
--- a/lib/schemas/package-detail-shape-schema.ts
+++ b/lib/schemas/package-detail-shape-schema.ts
@@ -115,6 +115,21 @@ export const RectSchema = BaseShapeSchema.extend({
rotation: z.number().optional(),
})
+export const TextSchema = BaseShapeSchema.extend({
+ type: z.literal("TEXT"),
+ text: z.string(),
+ x: tenthmil,
+ y: tenthmil,
+ size_mm: z.number(),
+ rotation: z.number().optional(),
+ layer: z.number().optional(),
+ textAnchor: z
+ .enum(["L", "C", "R", ""])
+ .optional()
+ .transform((val) => (val === "" ? undefined : val)),
+ font: z.string().optional(),
+})
+
export const PackageDetailShapeSchema = z.discriminatedUnion("type", [
TrackSchema,
PadSchema,
@@ -124,6 +139,7 @@ export const PackageDetailShapeSchema = z.discriminatedUnion("type", [
SVGNodeSchema,
HoleSchema,
RectSchema,
+ TextSchema,
])
const pairs = (arr: T[]): [T, T][] => {
@@ -290,6 +306,22 @@ export const ShapeItemSchema = z
fillStyle: fillStyle || undefined,
})
}
+ case "TEXT": {
+ const [textAnchor, x, y, size, layer, id, rotation, , font, text] =
+ shape.data.split("~")
+ return TextSchema.parse({
+ type: "TEXT",
+ text,
+ x,
+ y,
+ size_mm: Number(size) * 2.54, // empirically this seems to match, C5248081 is a good test case
+ layer: layer ? Number(layer) : undefined,
+ id,
+ rotation: rotation ? Number(rotation) : undefined,
+ textAnchor: textAnchor as "L" | "C" | "R" | undefined,
+ font: font || undefined,
+ })
+ }
default:
throw new Error(`Unknown shape type: ${shape.type}`)
diff --git a/tests/assets/C5248081.raweasy.json b/tests/assets/C5248081.raweasy.json
new file mode 100644
index 0000000..16d9237
--- /dev/null
+++ b/tests/assets/C5248081.raweasy.json
@@ -0,0 +1,201 @@
+{
+ "uuid": "333912d2af914b89a59f1086fd452842",
+ "title": "HS91L02W2C01",
+ "description": "",
+ "docType": 2,
+ "type": 3,
+ "thumb": "/component/333912d2af914b89a59f1086fd452842.png",
+ "lcsc": {
+ "id": 5960632,
+ "number": "C5248081"
+ },
+ "szlcsc": {
+ "id": 5960632,
+ "number": "C5248081"
+ },
+ "owner": {
+ "uuid": "0819f05c4eef4c71ace90d822a990e87",
+ "username": "lcsc",
+ "nickname": "LCSC",
+ "avatar": "//image.lceda.cn/avatars/2018/6/kFlrasi7W06gTdBLAqW3fkrqbDhbowynuSzkjqso.png"
+ },
+ "tags": ["OLED Displays Modules"],
+ "updateTime": 1705041603,
+ "updated_at": "2025-01-02 11:21:31",
+ "dataStr": {
+ "head": {
+ "docType": "2",
+ "editorVersion": "6.5.39",
+ "x": 395,
+ "y": 300,
+ "c_para": {
+ "pre": "OLED?",
+ "name": "HS91L02W2C01",
+ "package": "OLED-TH_L38.0-W12.0_HS91L02W2C01",
+ "Supplier": "LCSC",
+ "Supplier Part": "C5248081",
+ "Manufacturer": "HS(汉昇)",
+ "Manufacturer Part": "HS91L02W2C01",
+ "JLCPCB Part Class": "Extended Part",
+ "Contributor": "lcsc"
+ },
+ "puuid": "47a89f7083cc4ae28e8c42ffc894fa62",
+ "utime": 1702520650,
+ "uuid": "333912d2af914b89a59f1086fd452842",
+ "importFlag": 0,
+ "c_spiceCmd": null,
+ "pre": "OLED?",
+ "name": "HS91L02W2C01",
+ "hasIdFlag": true
+ },
+ "canvas": "CA~1000~1000~#FFFFFF~yes~#CCCCCC~5~1000~1000~line~5~pixel~5~395~300",
+ "shape": [
+ "R~375~275~2~2~30~50~#880000~1~0~none~gge1~0~",
+ "P~show~0~1~415~315~0~gge5~0^^415~315^^M 415 315 h -10~#000000^^1~401.3~319~0~GND~end~~~#000000^^1~405.5~314~0~1~start~~~#000000^^0~408~315^^0~M 405 312 L 402 315 L 405 318",
+ "P~show~0~2~415~305~0~gge6~0^^415~305^^M 415 305 h -10~#FF0000^^1~401.3~309~0~VCC~end~~~#FF0000^^1~405.5~304~0~2~start~~~#FF0000^^0~408~305^^0~M 405 302 L 402 305 L 405 308",
+ "P~show~0~3~415~295~0~gge7~0^^415~295^^M 415 295 h -10~#880000^^1~401.3~299~0~SCL~end~~~#0000FF^^1~405.5~294~0~3~start~~~#0000FF^^0~408~295^^0~M 405 292 L 402 295 L 405 298",
+ "P~show~0~4~415~285~0~gge8~0^^415~285^^M 415 285 h -10~#880000^^1~401.3~289~0~SDA~end~~~#0000FF^^1~405.5~284~0~4~start~~~#0000FF^^0~408~285^^0~M 405 282 L 402 285 L 405 288"
+ ],
+ "BBox": {
+ "x": 375,
+ "y": 274.6,
+ "width": 41,
+ "height": 50.4
+ },
+ "colors": []
+ },
+ "jlcOnSale": 1,
+ "datastrid": "cf2b344e7c2841908eba14c2cc47cfbb",
+ "verify": true,
+ "SMT": true,
+ "writable": false,
+ "isFavorite": false,
+ "packageDetail": {
+ "uuid": "47a89f7083cc4ae28e8c42ffc894fa62",
+ "title": "OLED-TH_L38.0-W12.0_HS91L02W2C01",
+ "docType": 4,
+ "updateTime": 1702952943,
+ "owner": {
+ "uuid": "0819f05c4eef4c71ace90d822a990e87",
+ "username": "lcsc",
+ "nickname": "LCSC",
+ "avatar": "//image.lceda.cn/avatars/2018/6/kFlrasi7W06gTdBLAqW3fkrqbDhbowynuSzkjqso.png"
+ },
+ "datastrid": "489da674c2524cb491d0ca3de342d9d7",
+ "writable": false,
+ "dataStr": {
+ "head": {
+ "docType": "4",
+ "editorVersion": "6.5.39",
+ "newgId": true,
+ "c_para": {
+ "package": "OLED-TH_L38.0-W12.0_HS91L02W2C01",
+ "pre": "OLED?",
+ "Contributor": "lcsc",
+ "link": "https://atta.szlcsc.com/upload/public/pdf/source/20230727/C3E43C8E3DDA847F5681FEE7D95C153D.pdf",
+ "3DModel": "OLED-TH_L38.0-W12.0_HS91L02W2C01"
+ },
+ "x": 4000,
+ "y": 3005,
+ "hasIdFlag": true,
+ "utime": 1702951613,
+ "uuid": "47a89f7083cc4ae28e8c42ffc894fa62",
+ "importFlag": 0,
+ "transformList": "",
+ "uuid_3d": "ce577b6b48bc443fbd8e9fda4477079f"
+ },
+ "canvas": "CA~1000~1000~#000000~yes~#FFFFFF~10~1000~1000~line~0.003937~mm~1~45~visible~0.5~4000~3005~0~none",
+ "shape": [
+ "SOLIDREGION~99~~M 3994.0945 2981.378 L 3994.0945 3028.622 L 4143.7005 3028.622 L 4143.7005 2981.378 Z ~solid~gge109~~~~0",
+ "TRACK~1~3~~3994.0945 2981.378 4143.7005 2981.378 4143.7005 3028.622 3994.0945 3028.622 3994.0945 2981.378~gge121~0",
+ "TEXT~L~4006.532~3027.35~0.8~0~0~3~~8~1~M 4006.5284 3020.7299 L 4007.2584 3020.3699 L 4008.3484 3019.2799 L 4008.3484 3026.9099~~gge133~~0~pinpart",
+ "PAD~ELLIPSE~4000~3020~6.2992~6.2992~11~~1~1.9685~~0~gge16~0~~Y~0~0~0.2~4000,3020",
+ "PAD~ELLIPSE~4000~3010~6.2992~6.2992~11~~2~1.9685~~0~gge46~0~~Y~0~0~0.2~4000,3010",
+ "PAD~ELLIPSE~4000~3000~6.2992~6.2992~11~~3~1.9685~~0~gge64~0~~Y~0~0~0.2~4000,3000",
+ "PAD~ELLIPSE~4000~2990~6.2992~6.2992~11~~4~1.9685~~0~gge82~0~~Y~0~0~0.2~4000,2990",
+ "SVGNODE~{\"gId\":\"g1_outline\",\"nodeName\":\"g\",\"nodeType\":1,\"layerid\":\"19\",\"attrs\":{\"c_width\":\"149.606\",\"c_height\":\"47.244\",\"c_rotation\":\"0,0,0\",\"z\":\"0\",\"c_origin\":\"4068.8975,3005\",\"uuid\":\"9018832d564840e08b96db89bf75c8cc\",\"c_etype\":\"outline3D\",\"id\":\"g1_outline\",\"title\":\"OLED-TH_L38.0-W12.0_HS91L02W2C01\",\"layerid\":\"19\",\"transform\":\"scale(1) translate(0, 0)\"},\"childNodes\":[{\"gId\":\"g1_outline_line0\",\"nodeName\":\"polyline\",\"nodeType\":1,\"attrs\":{\"fill\":\"none\",\"id\":\"g1_outline_line0\",\"c_shapetype\":\"line\",\"points\":\"3994.0945 2981.378 3994.0945 3014.8031 3994.0945 3028.622 4003.7401 3028.622 4143.7005 3028.622 4143.7005 3014.8031 4143.7005 2981.378 4003.7401 2981.378 3994.0945 2981.378 3994.0945 2981.378\"}}]}"
+ ],
+ "layers": [
+ "1~TopLayer~#FF0000~true~false~true~",
+ "2~BottomLayer~#0000FF~true~false~true~",
+ "3~TopSilkLayer~#FFCC00~true~true~true~",
+ "4~BottomSilkLayer~#66CC33~true~false~true~",
+ "5~TopPasteMaskLayer~#808080~true~false~true~",
+ "6~BottomPasteMaskLayer~#800000~true~false~true~",
+ "7~TopSolderMaskLayer~#800080~true~false~true~0.3",
+ "8~BottomSolderMaskLayer~#AA00FF~true~false~true~0.3",
+ "9~Ratlines~#6464FF~false~false~true~",
+ "10~BoardOutLine~#FF00FF~true~false~true~",
+ "11~Multi-Layer~#C0C0C0~true~false~true~",
+ "12~Document~#FFFFFF~true~false~true~",
+ "13~TopAssembly~#33CC99~true~false~true~",
+ "14~BottomAssembly~#5555FF~true~false~true~",
+ "15~Mechanical~#F022F0~true~false~true~",
+ "19~3DModel~#66CCFF~true~false~true~",
+ "21~Inner1~#999966~false~false~false~~",
+ "22~Inner2~#008000~false~false~false~~",
+ "23~Inner3~#00FF00~false~false~false~~",
+ "24~Inner4~#BC8E00~false~false~false~~",
+ "25~Inner5~#70DBFA~false~false~false~~",
+ "26~Inner6~#00CC66~false~false~false~~",
+ "27~Inner7~#9966FF~false~false~false~~",
+ "28~Inner8~#800080~false~false~false~~",
+ "29~Inner9~#008080~false~false~false~~",
+ "30~Inner10~#15935F~false~false~false~~",
+ "31~Inner11~#000080~false~false~false~~",
+ "32~Inner12~#00B400~false~false~false~~",
+ "33~Inner13~#2E4756~false~false~false~~",
+ "34~Inner14~#99842F~false~false~false~~",
+ "35~Inner15~#FFFFAA~false~false~false~~",
+ "36~Inner16~#99842F~false~false~false~~",
+ "37~Inner17~#2E4756~false~false~false~~",
+ "38~Inner18~#3535FF~false~false~false~~",
+ "39~Inner19~#8000BC~false~false~false~~",
+ "40~Inner20~#43AE5F~false~false~false~~",
+ "41~Inner21~#C3ECCE~false~false~false~~",
+ "42~Inner22~#728978~false~false~false~~",
+ "43~Inner23~#39503F~false~false~false~~",
+ "44~Inner24~#0C715D~false~false~false~~",
+ "45~Inner25~#5A8A80~false~false~false~~",
+ "46~Inner26~#2B937E~false~false~false~~",
+ "47~Inner27~#23999D~false~false~false~~",
+ "48~Inner28~#45B4E3~false~false~false~~",
+ "49~Inner29~#215DA1~false~false~false~~",
+ "50~Inner30~#4564D7~false~false~false~~",
+ "51~Inner31~#6969E9~false~false~false~~",
+ "52~Inner32~#9069E9~false~false~false~~",
+ "99~ComponentShapeLayer~#00CCCC~true~false~true~0.4",
+ "100~LeadShapeLayer~#CC9999~true~false~true~",
+ "101~ComponentMarkingLayer~#66FFCC~true~false~true~",
+ "Hole~Hole~#222222~false~false~true~",
+ "DRCError~DRCError~#FAD609~false~false~true~"
+ ],
+ "objects": [
+ "All~true~false",
+ "Component~true~true",
+ "Prefix~true~true",
+ "Name~true~false",
+ "Track~true~true",
+ "Pad~true~true",
+ "Via~true~true",
+ "Hole~true~true",
+ "Copper_Area~true~true",
+ "Circle~true~true",
+ "Arc~true~true",
+ "Solid_Region~true~true",
+ "Text~true~true",
+ "Image~true~true",
+ "Rect~true~true",
+ "Dimension~true~true",
+ "Protractor~true~true"
+ ],
+ "BBox": {
+ "x": 3994.1,
+ "y": 2981.4,
+ "width": 149.6,
+ "height": 47.2
+ },
+ "netColors": []
+ }
+ }
+}
diff --git a/tests/convert-to-ts/C5248081-to-ts.test.ts b/tests/convert-to-ts/C5248081-to-ts.test.ts
new file mode 100644
index 0000000..1d0f3a8
--- /dev/null
+++ b/tests/convert-to-ts/C5248081-to-ts.test.ts
@@ -0,0 +1,74 @@
+import { it, expect } from "bun:test"
+import chipRawEasy from "../assets/C5248081.raweasy.json"
+import { convertBetterEasyToTsx } from "lib/convert-to-typescript-component"
+import { EasyEdaJsonSchema } from "lib/schemas/easy-eda-json-schema"
+
+it("should convert C5248081 into typescript file", async () => {
+ const betterEasy = EasyEdaJsonSchema.parse(chipRawEasy)
+ const result = await convertBetterEasyToTsx({
+ betterEasy,
+ })
+
+ expect(result).not.toContain("milmm")
+ expect(result).not.toContain("NaNmm")
+
+ // Add more specific assertions here based on the component
+
+ expect(result).toMatchInlineSnapshot(`
+"import { createUseComponent } from "@tscircuit/core"
+import type { CommonLayoutProps } from "@tscircuit/props"
+
+const pinLabels = {
+ "pin1": [
+ "pin1",
+ "GND"
+ ],
+ "pin2": [
+ "pin2",
+ "VCC"
+ ],
+ "pin3": [
+ "pin3",
+ "SCL"
+ ],
+ "pin4": [
+ "pin4",
+ "SDA"
+ ]
+} as const
+
+interface Props extends CommonLayoutProps {
+ name: string
+}
+
+export const HS91L02W2C01 = (props: Props) => {
+ return (
+
+
+
+
+
+
+
+ }
+ />
+ )
+}
+
+export const useHS91L02W2C01 = createUseComponent(HS91L02W2C01, pinLabels)"
+`)
+})