circuit-to-canvas 0.0.6 → 0.0.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -41,7 +41,6 @@ interface CanvasContext {
41
41
  };
42
42
  font: string;
43
43
  textAlign: "start" | "end" | "left" | "right" | "center";
44
- textBaseline: "top" | "hanging" | "middle" | "alphabetic" | "ideographic" | "bottom";
45
44
  }
46
45
  type CopperLayerName = "top" | "bottom" | "inner1" | "inner2" | "inner3" | "inner4" | "inner5" | "inner6";
47
46
  type CopperColorMap = Record<CopperLayerName, string> & {
@@ -210,8 +209,6 @@ type AlphabetLayout = {
210
209
  letterSpacing: number;
211
210
  spaceWidth: number;
212
211
  strokeWidth: number;
213
- baselineOffset: number;
214
- descenderDepth: number;
215
212
  };
216
213
  declare function getAlphabetLayout(text: string, fontSize: number): AlphabetLayout;
217
214
  type AnchorAlignment = "center" | "top_left" | "top_right" | "bottom_left" | "bottom_right" | "left" | "right" | "top" | "bottom";
package/dist/index.js CHANGED
@@ -659,7 +659,6 @@ function drawPcbSilkscreenText(params) {
659
659
  ctx.font = `${fontSize}px sans-serif`;
660
660
  ctx.fillStyle = color;
661
661
  ctx.textAlign = mapAnchorAlignment(text.anchor_alignment);
662
- ctx.textBaseline = "middle";
663
662
  ctx.fillText(text.text, 0, 0);
664
663
  ctx.restore();
665
664
  }
@@ -821,16 +820,6 @@ var LETTER_SPACING_RATIO = 0.3;
821
820
  var SPACE_WIDTH_RATIO = 1;
822
821
  var STROKE_WIDTH_RATIO = 0.13;
823
822
  var CURVED_GLYPHS = /* @__PURE__ */ new Set(["O", "o", "0"]);
824
- var LOWERCASE_BASELINE_OFFSET = (() => {
825
- const referenceLetters = ["a", "c", "e", "m", "n", "o", "r", "s", "u", "x"];
826
- const offsets = referenceLetters.map((letter) => lineAlphabet[letter]).filter(
827
- (lines) => lines !== void 0 && lines.length > 0
828
- ).map(
829
- (lines) => Math.min(...lines.map((line) => Math.min(line.y1, line.y2)))
830
- );
831
- return offsets.length > 0 ? Math.min(...offsets) : 0;
832
- })();
833
- var getBaselineOffsetForLetter = (letter) => letter >= "a" && letter <= "z" ? LOWERCASE_BASELINE_OFFSET : 0;
834
823
  function getAlphabetLayout(text, fontSize) {
835
824
  const glyphWidth = fontSize * GLYPH_WIDTH_RATIO;
836
825
  const letterSpacing = glyphWidth * LETTER_SPACING_RATIO;
@@ -843,24 +832,19 @@ function getAlphabetLayout(text, fontSize) {
843
832
  if (index < characters.length - 1) width += letterSpacing;
844
833
  });
845
834
  const strokeWidth = Math.max(fontSize * STROKE_WIDTH_RATIO, 0.35);
846
- const hasLowercase = /[a-z]/.test(text);
847
- const baselineOffset = hasLowercase ? (1 - LOWERCASE_BASELINE_OFFSET) * fontSize : fontSize;
848
- const descenderDepth = hasLowercase ? LOWERCASE_BASELINE_OFFSET * fontSize : 0;
849
835
  return {
850
836
  width,
851
837
  height: fontSize,
852
838
  glyphWidth,
853
839
  letterSpacing,
854
840
  spaceWidth,
855
- strokeWidth,
856
- baselineOffset,
857
- descenderDepth
841
+ strokeWidth
858
842
  };
859
843
  }
860
844
  var getGlyphLines = (char) => lineAlphabet[char] ?? lineAlphabet[char.toUpperCase()];
861
845
  function getTextStartPosition(alignment, layout) {
862
846
  const totalWidth = layout.width + layout.strokeWidth;
863
- const totalHeight = layout.height + layout.descenderDepth + layout.strokeWidth;
847
+ const totalHeight = layout.height + layout.strokeWidth;
864
848
  let x = 0;
865
849
  let y = 0;
866
850
  if (alignment === "center") {
@@ -871,36 +855,27 @@ function getTextStartPosition(alignment, layout) {
871
855
  x = -totalWidth;
872
856
  }
873
857
  if (alignment === "center") {
874
- y = layout.baselineOffset - layout.height / 2;
858
+ y = -totalHeight / 2;
875
859
  } else if (alignment === "top_left" || alignment === "top_right" || alignment === "top") {
876
- y = layout.baselineOffset;
860
+ y = 0;
877
861
  } else if (alignment === "bottom_left" || alignment === "bottom_right" || alignment === "bottom") {
878
- y = -layout.descenderDepth;
862
+ y = -totalHeight;
879
863
  } else {
880
864
  y = 0;
881
865
  }
882
866
  return { x, y };
883
867
  }
884
868
  function strokeAlphabetText(ctx, text, layout, startX, startY) {
885
- const {
886
- glyphWidth,
887
- letterSpacing,
888
- spaceWidth,
889
- height,
890
- strokeWidth,
891
- baselineOffset
892
- } = layout;
893
- const baselineY = startY;
869
+ const { glyphWidth, letterSpacing, spaceWidth, height, strokeWidth } = layout;
870
+ const topY = startY;
894
871
  const characters = Array.from(text);
895
872
  let cursor = startX + strokeWidth / 2;
896
873
  characters.forEach((char, index) => {
897
874
  const lines = getGlyphLines(char);
898
875
  const advance = char === " " ? spaceWidth : glyphWidth;
899
- const charBaselineOffset = getBaselineOffsetForLetter(char);
900
876
  if (CURVED_GLYPHS.has(char)) {
901
877
  const normalizedCenterY = 0.5;
902
- const adjustedCenterY = normalizedCenterY - charBaselineOffset;
903
- const centerY = baselineY - adjustedCenterY * height;
878
+ const centerY = topY + normalizedCenterY * height;
904
879
  const radiusX = Math.max(glyphWidth / 2 - strokeWidth / 2, strokeWidth);
905
880
  const radiusY = Math.max(height / 2 - strokeWidth / 2, strokeWidth);
906
881
  ctx.beginPath();
@@ -917,12 +892,10 @@ function strokeAlphabetText(ctx, text, layout, startX, startY) {
917
892
  } else if (lines?.length) {
918
893
  ctx.beginPath();
919
894
  for (const line of lines) {
920
- const adjusted_y1 = line.y1 - charBaselineOffset;
921
- const adjusted_y2 = line.y2 - charBaselineOffset;
922
895
  const x1 = cursor + line.x1 * glyphWidth;
923
- const y1 = baselineY - adjusted_y1 * height;
896
+ const y1 = topY + (1 - line.y1) * height;
924
897
  const x2 = cursor + line.x2 * glyphWidth;
925
- const y2 = baselineY - adjusted_y2 * height;
898
+ const y2 = topY + (1 - line.y2) * height;
926
899
  ctx.moveTo(x1, y1);
927
900
  ctx.lineTo(x2, y2);
928
901
  }
@@ -1010,8 +983,8 @@ function drawPcbCopperText(params) {
1010
983
  const paddingRight = padding.right * scale2;
1011
984
  const paddingTop = padding.top * scale2;
1012
985
  const paddingBottom = padding.bottom * scale2;
1013
- const textBoxTop = startY - layout.baselineOffset - layout.strokeWidth / 2;
1014
- const textBoxBottom = startY + layout.descenderDepth + layout.strokeWidth / 2;
986
+ const textBoxTop = startY - layout.strokeWidth / 2;
987
+ const textBoxBottom = startY + layout.height + layout.strokeWidth / 2;
1015
988
  const textBoxHeight = textBoxBottom - textBoxTop;
1016
989
  const xOffset = startX - paddingLeft;
1017
990
  const yOffset = textBoxTop - paddingTop;
@@ -72,10 +72,8 @@ export function drawPcbCopperText(params: DrawPcbCopperTextParams): void {
72
72
  const paddingTop = padding.top * scale
73
73
  const paddingBottom = padding.bottom * scale
74
74
  // Calculate knockout rectangle to cover the text box
75
- // startY is the baseline position, text box extends from (baseline - baselineOffset) to (baseline + descenderDepth)
76
- const textBoxTop = startY - layout.baselineOffset - layout.strokeWidth / 2
77
- const textBoxBottom =
78
- startY + layout.descenderDepth + layout.strokeWidth / 2
75
+ const textBoxTop = startY - layout.strokeWidth / 2
76
+ const textBoxBottom = startY + layout.height + layout.strokeWidth / 2
79
77
  const textBoxHeight = textBoxBottom - textBoxTop
80
78
 
81
79
  const xOffset = startX - paddingLeft
@@ -88,7 +88,6 @@ export function drawPcbSilkscreenText(
88
88
  ctx.font = `${fontSize}px sans-serif`
89
89
  ctx.fillStyle = color
90
90
  ctx.textAlign = mapAnchorAlignment(text.anchor_alignment)
91
- ctx.textBaseline = "middle"
92
91
  ctx.fillText(text.text, 0, 0)
93
92
  ctx.restore()
94
93
  }
@@ -9,25 +9,6 @@ const SPACE_WIDTH_RATIO = 1
9
9
  const STROKE_WIDTH_RATIO = 0.13
10
10
  const CURVED_GLYPHS = new Set(["O", "o", "0"])
11
11
 
12
- // Calculate baseline offset from reference lowercase letters (same as working implementation)
13
- const LOWERCASE_BASELINE_OFFSET = (() => {
14
- const referenceLetters = ["a", "c", "e", "m", "n", "o", "r", "s", "u", "x"]
15
- const offsets = referenceLetters
16
- .map((letter) => lineAlphabet[letter])
17
- .filter(
18
- (lines): lines is NonNullable<typeof lines> =>
19
- lines !== undefined && lines.length > 0,
20
- )
21
- .map((lines) =>
22
- Math.min(...lines.map((line) => Math.min(line.y1, line.y2))),
23
- )
24
- return offsets.length > 0 ? Math.min(...offsets) : 0
25
- })()
26
-
27
- // Get baseline offset for a specific letter (only lowercase letters get offset)
28
- const getBaselineOffsetForLetter = (letter: string) =>
29
- letter >= "a" && letter <= "z" ? LOWERCASE_BASELINE_OFFSET : 0
30
-
31
12
  export type AlphabetLayout = {
32
13
  width: number
33
14
  height: number
@@ -35,8 +16,6 @@ export type AlphabetLayout = {
35
16
  letterSpacing: number
36
17
  spaceWidth: number
37
18
  strokeWidth: number
38
- baselineOffset: number // Distance from top to baseline
39
- descenderDepth: number // Distance from baseline to bottom (for descenders)
40
19
  }
41
20
 
42
21
  export function getAlphabetLayout(
@@ -56,15 +35,6 @@ export function getAlphabetLayout(
56
35
  })
57
36
 
58
37
  const strokeWidth = Math.max(fontSize * STROKE_WIDTH_RATIO, 0.35)
59
- // Calculate baseline offset: distance from top of text box to baseline
60
- // In normalized coords: y=0 is bottom, y=1 is top
61
- // LOWERCASE_BASELINE_OFFSET is the minimum y (baseline position) in normalized coords
62
- const hasLowercase = /[a-z]/.test(text)
63
- const baselineOffset = hasLowercase
64
- ? (1 - LOWERCASE_BASELINE_OFFSET) * fontSize
65
- : fontSize
66
- // Descender depth: distance from baseline to bottom (for descenders like g, j, p, q, y)
67
- const descenderDepth = hasLowercase ? LOWERCASE_BASELINE_OFFSET * fontSize : 0
68
38
 
69
39
  return {
70
40
  width,
@@ -73,8 +43,6 @@ export function getAlphabetLayout(
73
43
  letterSpacing,
74
44
  spaceWidth,
75
45
  strokeWidth,
76
- baselineOffset,
77
- descenderDepth,
78
46
  }
79
47
  }
80
48
 
@@ -97,8 +65,7 @@ export function getTextStartPosition(
97
65
  layout: AlphabetLayout,
98
66
  ): { x: number; y: number } {
99
67
  const totalWidth = layout.width + layout.strokeWidth
100
- // Total height includes descender depth for proper vertical alignment
101
- const totalHeight = layout.height + layout.descenderDepth + layout.strokeWidth
68
+ const totalHeight = layout.height + layout.strokeWidth
102
69
 
103
70
  let x = 0
104
71
  let y = 0
@@ -120,22 +87,21 @@ export function getTextStartPosition(
120
87
  x = -totalWidth
121
88
  }
122
89
 
123
- // Vertical alignment - positions relative to baseline
124
- // Text extends from (baseline - baselineOffset) at top to (baseline + descenderDepth) at bottom
90
+ // Vertical alignment
125
91
  if (alignment === "center") {
126
- y = layout.baselineOffset - layout.height / 2
92
+ y = -totalHeight / 2
127
93
  } else if (
128
94
  alignment === "top_left" ||
129
95
  alignment === "top_right" ||
130
96
  alignment === "top"
131
97
  ) {
132
- y = layout.baselineOffset
98
+ y = 0
133
99
  } else if (
134
100
  alignment === "bottom_left" ||
135
101
  alignment === "bottom_right" ||
136
102
  alignment === "bottom"
137
103
  ) {
138
- y = -layout.descenderDepth
104
+ y = -totalHeight
139
105
  } else {
140
106
  y = 0
141
107
  }
@@ -150,29 +116,18 @@ export function strokeAlphabetText(
150
116
  startX: number,
151
117
  startY: number,
152
118
  ): void {
153
- const {
154
- glyphWidth,
155
- letterSpacing,
156
- spaceWidth,
157
- height,
158
- strokeWidth,
159
- baselineOffset,
160
- } = layout
161
- const baselineY = startY
119
+ const { glyphWidth, letterSpacing, spaceWidth, height, strokeWidth } = layout
120
+ const topY = startY
162
121
  const characters = Array.from(text)
163
122
  let cursor = startX + strokeWidth / 2
164
123
 
165
124
  characters.forEach((char, index) => {
166
125
  const lines = getGlyphLines(char)
167
126
  const advance = char === " " ? spaceWidth : glyphWidth
168
- // Get normalized baseline offset for this specific character (0-1 range)
169
- const charBaselineOffset = getBaselineOffsetForLetter(char)
170
127
 
171
128
  if (CURVED_GLYPHS.has(char)) {
172
- // For curved glyphs, adjust coordinates by baseline offset
173
129
  const normalizedCenterY = 0.5
174
- const adjustedCenterY = normalizedCenterY - charBaselineOffset
175
- const centerY = baselineY - adjustedCenterY * height
130
+ const centerY = topY + normalizedCenterY * height
176
131
  const radiusX = Math.max(glyphWidth / 2 - strokeWidth / 2, strokeWidth)
177
132
  const radiusY = Math.max(height / 2 - strokeWidth / 2, strokeWidth)
178
133
  ctx.beginPath()
@@ -190,12 +145,11 @@ export function strokeAlphabetText(
190
145
  ctx.beginPath()
191
146
  for (const line of lines) {
192
147
  // Convert normalized y coordinates to canvas coordinates (inverted for canvas)
193
- const adjusted_y1 = line.y1 - charBaselineOffset
194
- const adjusted_y2 = line.y2 - charBaselineOffset
148
+ // In normalized coords: y=0 is bottom, y=1 is top
195
149
  const x1 = cursor + line.x1 * glyphWidth
196
- const y1 = baselineY - adjusted_y1 * height
150
+ const y1 = topY + (1 - line.y1) * height
197
151
  const x2 = cursor + line.x2 * glyphWidth
198
- const y2 = baselineY - adjusted_y2 * height
152
+ const y2 = topY + (1 - line.y2) * height
199
153
  ctx.moveTo(x1, y1)
200
154
  ctx.lineTo(x2, y2)
201
155
  }
@@ -51,13 +51,6 @@ export interface CanvasContext {
51
51
  }
52
52
  font: string
53
53
  textAlign: "start" | "end" | "left" | "right" | "center"
54
- textBaseline:
55
- | "top"
56
- | "hanging"
57
- | "middle"
58
- | "alphabetic"
59
- | "ideographic"
60
- | "bottom"
61
54
  }
62
55
 
63
56
  export type CopperLayerName =
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "circuit-to-canvas",
3
3
  "main": "dist/index.js",
4
- "version": "0.0.6",
4
+ "version": "0.0.7",
5
5
  "type": "module",
6
6
  "scripts": {
7
7
  "build": "tsup-node ./lib/index.ts --format esm --dts",
@@ -10,7 +10,7 @@
10
10
  },
11
11
  "devDependencies": {
12
12
  "@biomejs/biome": "^2.3.8",
13
- "@tscircuit/alphabet": "^0.0.8",
13
+ "@tscircuit/alphabet": "^0.0.9",
14
14
  "@types/bun": "latest",
15
15
  "bun-match-svg": "^0.0.14",
16
16
  "canvas": "^3.2.0",
@@ -20,7 +20,7 @@ test("draw copper text", async () => {
20
20
  pcb_copper_text_id: "copper-text-1",
21
21
  pcb_component_id: "component1",
22
22
  layer: "top",
23
- text: "T1",
23
+ text: "AabcbCdde",
24
24
  anchor_position: { x: 40, y: 40 },
25
25
  anchor_alignment: "center",
26
26
  font: "tscircuit2024",
@@ -13,7 +13,7 @@ test("draw lowercase text with descenders", async () => {
13
13
  ctx.fillStyle = "#1a1a1a"
14
14
  ctx.fillRect(0, 0, canvas.width / SCALE, canvas.height / SCALE)
15
15
 
16
- // Draw baseline reference line
16
+ // Draw reference line
17
17
  ctx.strokeStyle = "#666666"
18
18
  ctx.lineWidth = 0.5
19
19
  ctx.beginPath()
@@ -0,0 +1,84 @@
1
+ import { expect, test } from "bun:test"
2
+ import { createCanvas } from "canvas"
3
+ import type { PcbFabricationNoteText } from "circuit-json"
4
+ import { CircuitToCanvasDrawer } from "../../lib/drawer"
5
+
6
+ test("draw full character set", async () => {
7
+ const SCALE = 4
8
+ const canvas = createCanvas(800 * SCALE, 300 * SCALE)
9
+ const ctx = canvas.getContext("2d")
10
+ ctx.scale(SCALE, SCALE)
11
+ const drawer = new CircuitToCanvasDrawer(ctx)
12
+
13
+ ctx.fillStyle = "#1a1a1a"
14
+ ctx.fillRect(0, 0, canvas.width / SCALE, canvas.height / SCALE)
15
+
16
+ const fontSize = 20
17
+ const lineHeight = 40
18
+ let y = 30
19
+
20
+ // Lowercase letters
21
+ const lowercaseText: PcbFabricationNoteText = {
22
+ type: "pcb_fabrication_note_text",
23
+ pcb_fabrication_note_text_id: "fab-note-lowercase",
24
+ pcb_component_id: "component1",
25
+ layer: "top",
26
+ text: "abcdefghijklmnopqrstuvwxyz",
27
+ anchor_position: { x: 400, y },
28
+ anchor_alignment: "center",
29
+ font: "tscircuit2024",
30
+ font_size: fontSize,
31
+ }
32
+ drawer.drawElements([lowercaseText])
33
+ y += lineHeight
34
+
35
+ // Uppercase letters
36
+ const uppercaseText: PcbFabricationNoteText = {
37
+ type: "pcb_fabrication_note_text",
38
+ pcb_fabrication_note_text_id: "fab-note-uppercase",
39
+ pcb_component_id: "component2",
40
+ layer: "top",
41
+ text: "ABCDEFGHIJKLMNOPQRSTUVWXYZ",
42
+ anchor_position: { x: 400, y },
43
+ anchor_alignment: "center",
44
+ font: "tscircuit2024",
45
+ font_size: fontSize,
46
+ }
47
+ drawer.drawElements([uppercaseText])
48
+ y += lineHeight
49
+
50
+ // Numbers
51
+ const numbersText: PcbFabricationNoteText = {
52
+ type: "pcb_fabrication_note_text",
53
+ pcb_fabrication_note_text_id: "fab-note-numbers",
54
+ pcb_component_id: "component3",
55
+ layer: "top",
56
+ text: "0123456789",
57
+ anchor_position: { x: 400, y },
58
+ anchor_alignment: "center",
59
+ font: "tscircuit2024",
60
+ font_size: fontSize,
61
+ }
62
+ drawer.drawElements([numbersText])
63
+ y += lineHeight
64
+
65
+ // Common symbols
66
+ const symbolsText: PcbFabricationNoteText = {
67
+ type: "pcb_fabrication_note_text",
68
+ pcb_fabrication_note_text_id: "fab-note-symbols",
69
+ pcb_component_id: "component4",
70
+ layer: "top",
71
+ text: "()!@#$%^&*",
72
+ anchor_position: { x: 400, y },
73
+ anchor_alignment: "center",
74
+ font: "tscircuit2024",
75
+ font_size: fontSize,
76
+ }
77
+ drawer.drawElements([symbolsText])
78
+ y += lineHeight
79
+
80
+ await expect(canvas.toBuffer("image/png")).toMatchPngSnapshot(
81
+ import.meta.path,
82
+ "fabrication-note-text-full-charset",
83
+ )
84
+ })
@@ -1,80 +0,0 @@
1
- import { expect, test } from "bun:test"
2
- import { createCanvas } from "canvas"
3
- import type { PcbFabricationNoteText } from "circuit-json"
4
- import { CircuitToCanvasDrawer } from "../../lib/drawer"
5
-
6
- test("draw text baseline alignment with different anchor positions", async () => {
7
- const SCALE = 4
8
- const canvas = createCanvas(300 * SCALE, 200 * SCALE)
9
- const ctx = canvas.getContext("2d")
10
- ctx.scale(SCALE, SCALE)
11
- const drawer = new CircuitToCanvasDrawer(ctx)
12
-
13
- ctx.fillStyle = "#1a1a1a"
14
- ctx.fillRect(0, 0, canvas.width / SCALE, canvas.height / SCALE)
15
-
16
- // Draw reference lines
17
- ctx.strokeStyle = "#444444"
18
- ctx.lineWidth = 0.5
19
- // Top line
20
- ctx.beginPath()
21
- ctx.moveTo(10, 50)
22
- ctx.lineTo(290, 50)
23
- ctx.stroke()
24
- // Center/baseline line
25
- ctx.beginPath()
26
- ctx.moveTo(10, 100)
27
- ctx.lineTo(290, 100)
28
- ctx.stroke()
29
- // Bottom line
30
- ctx.beginPath()
31
- ctx.moveTo(10, 150)
32
- ctx.lineTo(290, 150)
33
- ctx.stroke()
34
-
35
- // Test with top alignment
36
- const textTop: PcbFabricationNoteText = {
37
- type: "pcb_fabrication_note_text",
38
- pcb_fabrication_note_text_id: "fab-note-top",
39
- pcb_component_id: "component1",
40
- layer: "top",
41
- text: "gap",
42
- anchor_position: { x: 50, y: 50 },
43
- anchor_alignment: "top_left",
44
- font: "tscircuit2024",
45
- font_size: 16,
46
- }
47
-
48
- // Test with center alignment (baseline at center)
49
- const textCenter: PcbFabricationNoteText = {
50
- type: "pcb_fabrication_note_text",
51
- pcb_fabrication_note_text_id: "fab-note-center",
52
- pcb_component_id: "component2",
53
- layer: "top",
54
- text: "gap",
55
- anchor_position: { x: 150, y: 100 },
56
- anchor_alignment: "center",
57
- font: "tscircuit2024",
58
- font_size: 16,
59
- }
60
-
61
- // Test with bottom alignment
62
- const textBottom: PcbFabricationNoteText = {
63
- type: "pcb_fabrication_note_text",
64
- pcb_fabrication_note_text_id: "fab-note-bottom",
65
- pcb_component_id: "component3",
66
- layer: "top",
67
- text: "gap",
68
- anchor_position: { x: 250, y: 150 },
69
- anchor_alignment: "bottom_left",
70
- font: "tscircuit2024",
71
- font_size: 16,
72
- }
73
-
74
- drawer.drawElements([textTop, textCenter, textBottom])
75
-
76
- await expect(canvas.toBuffer("image/png")).toMatchPngSnapshot(
77
- import.meta.path,
78
- "fabrication-note-text-baseline-anchors",
79
- )
80
- })
@@ -1,43 +0,0 @@
1
- import { expect, test } from "bun:test"
2
- import { createCanvas } from "canvas"
3
- import type { PcbFabricationNoteText } from "circuit-json"
4
- import { CircuitToCanvasDrawer } from "../../lib/drawer"
5
-
6
- test("draw text with baseline alignment and descenders", async () => {
7
- const SCALE = 4
8
- const canvas = createCanvas(200 * SCALE, 150 * SCALE)
9
- const ctx = canvas.getContext("2d")
10
- ctx.scale(SCALE, SCALE)
11
- const drawer = new CircuitToCanvasDrawer(ctx)
12
-
13
- ctx.fillStyle = "#1a1a1a"
14
- ctx.fillRect(0, 0, canvas.width / SCALE, canvas.height / SCALE)
15
-
16
- // Draw baseline reference line
17
- ctx.strokeStyle = "#444444"
18
- ctx.lineWidth = 0.5
19
- ctx.beginPath()
20
- ctx.moveTo(10, 75)
21
- ctx.lineTo(190, 75)
22
- ctx.stroke()
23
-
24
- // Test text with lowercase letters and descenders (g, j, p, q, y)
25
- const text: PcbFabricationNoteText = {
26
- type: "pcb_fabrication_note_text",
27
- pcb_fabrication_note_text_id: "fab-note-baseline",
28
- pcb_component_id: "component1",
29
- layer: "top",
30
- text: "gap jqpy",
31
- anchor_position: { x: 100, y: 75 },
32
- anchor_alignment: "center",
33
- font: "tscircuit2024",
34
- font_size: 20,
35
- }
36
-
37
- drawer.drawElements([text])
38
-
39
- await expect(canvas.toBuffer("image/png")).toMatchPngSnapshot(
40
- import.meta.path,
41
- "fabrication-note-text-baseline",
42
- )
43
- })