circuitscript 0.0.29 → 0.0.32

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.
Files changed (64) hide show
  1. package/dist/cjs/BaseVisitor.js +6 -1
  2. package/dist/cjs/antlr/CircuitScriptLexer.js +204 -200
  3. package/dist/cjs/antlr/CircuitScriptParser.js +1066 -1173
  4. package/dist/cjs/draw_symbols.js +330 -87
  5. package/dist/cjs/execute.js +41 -14
  6. package/dist/cjs/geometry.js +79 -18
  7. package/dist/cjs/globals.js +37 -6
  8. package/dist/cjs/helpers.js +75 -5
  9. package/dist/cjs/layout.js +107 -43
  10. package/dist/cjs/main.js +10 -4
  11. package/dist/cjs/objects/ClassComponent.js +2 -0
  12. package/dist/cjs/objects/ExecutionScope.js +1 -1
  13. package/dist/cjs/objects/Frame.js +2 -0
  14. package/dist/cjs/objects/Net.js +3 -2
  15. package/dist/cjs/objects/PinTypes.js +7 -1
  16. package/dist/cjs/objects/types.js +11 -1
  17. package/dist/cjs/regenerate-tests.js +64 -3
  18. package/dist/cjs/render.js +29 -21
  19. package/dist/cjs/sizing.js +4 -6
  20. package/dist/cjs/utils.js +29 -5
  21. package/dist/cjs/visitor.js +176 -10
  22. package/dist/esm/BaseVisitor.mjs +6 -1
  23. package/dist/esm/antlr/CircuitScriptLexer.mjs +204 -200
  24. package/dist/esm/antlr/CircuitScriptParser.mjs +1061 -1171
  25. package/dist/esm/antlr/CircuitScriptVisitor.mjs +3 -0
  26. package/dist/esm/draw_symbols.mjs +324 -85
  27. package/dist/esm/execute.mjs +42 -15
  28. package/dist/esm/geometry.mjs +79 -17
  29. package/dist/esm/globals.mjs +36 -5
  30. package/dist/esm/helpers.mjs +74 -5
  31. package/dist/esm/layout.mjs +110 -46
  32. package/dist/esm/main.mjs +11 -5
  33. package/dist/esm/objects/ClassComponent.mjs +6 -0
  34. package/dist/esm/objects/ExecutionScope.mjs +1 -1
  35. package/dist/esm/objects/Frame.mjs +2 -0
  36. package/dist/esm/objects/Net.mjs +3 -2
  37. package/dist/esm/objects/PinTypes.mjs +6 -0
  38. package/dist/esm/objects/types.mjs +14 -0
  39. package/dist/esm/regenerate-tests.mjs +64 -3
  40. package/dist/esm/render.mjs +30 -22
  41. package/dist/esm/sizing.mjs +3 -4
  42. package/dist/esm/utils.mjs +26 -4
  43. package/dist/esm/visitor.mjs +179 -13
  44. package/dist/types/antlr/CircuitScriptLexer.d.ts +42 -41
  45. package/dist/types/antlr/CircuitScriptParser.d.ts +144 -133
  46. package/dist/types/antlr/CircuitScriptVisitor.d.ts +6 -0
  47. package/dist/types/draw_symbols.d.ts +15 -2
  48. package/dist/types/execute.d.ts +5 -4
  49. package/dist/types/geometry.d.ts +4 -3
  50. package/dist/types/globals.d.ts +32 -3
  51. package/dist/types/helpers.d.ts +12 -0
  52. package/dist/types/layout.d.ts +8 -2
  53. package/dist/types/objects/ClassComponent.d.ts +8 -0
  54. package/dist/types/objects/Frame.d.ts +3 -1
  55. package/dist/types/objects/PinTypes.d.ts +1 -0
  56. package/dist/types/objects/Wire.d.ts +4 -2
  57. package/dist/types/objects/types.d.ts +8 -0
  58. package/dist/types/render.d.ts +5 -1
  59. package/dist/types/sizing.d.ts +0 -4
  60. package/dist/types/utils.d.ts +3 -0
  61. package/dist/types/visitor.d.ts +8 -1
  62. package/fonts/Arial.ttf +0 -0
  63. package/libs/lib.cst +58 -41
  64. package/package.json +5 -1
@@ -3,7 +3,7 @@ import { ClassComponent } from './objects/ClassComponent.mjs';
3
3
  import { ActiveObject, ExecutionScope, FrameAction, SequenceAction } from './objects/ExecutionScope.mjs';
4
4
  import { Net } from './objects/Net.mjs';
5
5
  import { PortSide } from './objects/PinDefinition.mjs';
6
- import { Direction } from './objects/types.mjs';
6
+ import { DeclaredReference, Direction } from './objects/types.mjs';
7
7
  import { Wire } from './objects/Wire.mjs';
8
8
  import { Frame } from './objects/Frame.mjs';
9
9
  import { CalculatePinPositions } from './layout.mjs';
@@ -199,20 +199,20 @@ export class ExecutionContext {
199
199
  return component;
200
200
  }
201
201
  printPoint(extra = '') {
202
- let netName = NoNetText;
202
+ let netString = NoNetText;
203
203
  if (this.scope.currentComponent === null || this.scope.currentPin === null) {
204
204
  this.log((extra !== '' ? (extra + ' ') : '') + 'point is null');
205
205
  return;
206
206
  }
207
207
  if (this.scope.hasNet(this.scope.currentComponent, this.scope.currentPin)) {
208
- netName = this.scope
208
+ netString = this.scope
209
209
  .getNet(this.scope.currentComponent, this.scope.currentPin)
210
210
  .toString();
211
211
  }
212
212
  this.log((extra !== '' ? (extra + ' ') : '') + 'point: ' +
213
213
  this.scope.currentComponent.instanceName +
214
214
  ' ' +
215
- this.scope.currentPin + ' ' + netName);
215
+ this.scope.currentPin + ' ' + netString);
216
216
  }
217
217
  addComponentExisting(component, pin) {
218
218
  const startPin = pin;
@@ -466,34 +466,34 @@ export class ExecutionContext {
466
466
  for (let i = 0; i < reversed.length; i++) {
467
467
  const context = reversed[i];
468
468
  if (context.hasFunction(idName)) {
469
- return {
469
+ return new DeclaredReference({
470
470
  found: true,
471
471
  value: context.getFunction(idName),
472
472
  type: ReferenceTypes.function,
473
473
  name: idName,
474
- };
474
+ });
475
475
  }
476
476
  else if (context.scope.variables.has(idName)) {
477
- return {
477
+ return new DeclaredReference({
478
478
  found: true,
479
479
  value: context.scope.variables.get(idName),
480
480
  type: ReferenceTypes.variable,
481
481
  name: idName,
482
- };
482
+ });
483
483
  }
484
484
  else if (context.scope.instances.has(idName)) {
485
- return {
485
+ return new DeclaredReference({
486
486
  found: true,
487
487
  value: context.scope.instances.get(idName),
488
488
  type: ReferenceTypes.instance,
489
489
  name: idName,
490
- };
490
+ });
491
491
  }
492
492
  }
493
- return {
493
+ return new DeclaredReference({
494
494
  found: false,
495
495
  name: idName,
496
- };
496
+ });
497
497
  }
498
498
  callFunction(functionName, functionParams, executionStack, netNamespace) {
499
499
  let __runFunc = null;
@@ -520,6 +520,7 @@ export class ExecutionContext {
520
520
  }
521
521
  if (__runFunc !== null) {
522
522
  this.log(`call function '${functionName}'`);
523
+ this.log(`net namespace: ${netNamespace}`);
523
524
  const functionResult = __runFunc(functionParams, { netNamespace });
524
525
  this.log(`done call function '${functionName}'`);
525
526
  return functionResult;
@@ -652,6 +653,10 @@ export class ExecutionContext {
652
653
  this.scope.setActive(ActiveObject.Wire, wireId);
653
654
  this.scope.sequence.push([SequenceAction.Wire, wireId, tmp]);
654
655
  this.scope.currentComponent.pinWires.set(this.scope.currentPin, tmp);
656
+ if (!this.scope.currentComponent.didSetWireOrientationAngle) {
657
+ this.applyComponentAngleFromWire(this.scope.currentComponent, this.scope.currentPin, true);
658
+ this.scope.currentComponent.didSetWireOrientationAngle = true;
659
+ }
655
660
  }
656
661
  addPoint(pointId, userDefined = true) {
657
662
  if (this.scope.instances.has(pointId)) {
@@ -702,18 +707,37 @@ export class ExecutionContext {
702
707
  this.scope.currentComponent.styles[key] = styles[key];
703
708
  }
704
709
  }
705
- applyComponentAngleFromWire(component, pin) {
710
+ applyComponentAngleFromWire(component, pin, opposite = false) {
706
711
  if (this.componentAngleFollowsWire
707
712
  && component.followWireOrientationProp
708
713
  && component.useWireOrientationAngle
714
+ && !component.didSetWireOrientationAngle
709
715
  && this.scope.currentWireId !== -1) {
710
716
  const currentWire = this.scope.wires[this.scope.currentWireId];
711
- const lastSegment = currentWire.path[currentWire.path.length - 1];
717
+ let useSegment = currentWire.path[currentWire.path.length - 1];
718
+ if (opposite) {
719
+ useSegment = currentWire.path[0];
720
+ }
712
721
  const pinPositions = CalculatePinPositions(component);
713
722
  if (pinPositions.has(pin)) {
714
723
  const connectedPinPos = pinPositions.get(pin);
715
724
  let targetAngle = null;
716
- switch (lastSegment.direction) {
725
+ let useDirection = useSegment.direction;
726
+ if (opposite) {
727
+ if (useDirection === Direction.Down) {
728
+ useDirection = Direction.Up;
729
+ }
730
+ else if (useDirection === Direction.Up) {
731
+ useDirection = Direction.Down;
732
+ }
733
+ else if (useDirection === Direction.Right) {
734
+ useDirection = Direction.Left;
735
+ }
736
+ else if (useDirection === Direction.Left) {
737
+ useDirection = Direction.Right;
738
+ }
739
+ }
740
+ switch (useDirection) {
717
741
  case Direction.Down:
718
742
  targetAngle = 90;
719
743
  break;
@@ -752,12 +776,14 @@ export class ExecutionContext {
752
776
  component.setParam('angle', 270);
753
777
  }
754
778
  component.wireOrientationAngle = useAngle;
779
+ component.didSetWireOrientationAngle = true;
755
780
  }
756
781
  }
757
782
  }
758
783
  enterFrame() {
759
784
  const frameId = this.scope.frames.length + 1;
760
785
  const frameObject = new Frame(frameId);
786
+ this.log('Enter frame', frameId);
761
787
  this.scope.frames.push(frameObject);
762
788
  this.scope.sequence.push([SequenceAction.Frame,
763
789
  frameObject, FrameAction.Enter]);
@@ -769,6 +795,7 @@ export class ExecutionContext {
769
795
  const frame = this.scope.frames[frameId - 1];
770
796
  this.scope.sequence.push([SequenceAction.Frame,
771
797
  frame, FrameAction.Exit]);
798
+ this.log('Leave frame', frameId);
772
799
  }
773
800
  }
774
801
  function isWireSegmentsEndAuto(segments) {
@@ -1,7 +1,9 @@
1
1
  import Flatten from '@flatten-js/core';
2
2
  import { measureTextSize2 } from './sizing.mjs';
3
- import { defaultFont } from './globals.mjs';
3
+ import { defaultFont, fontDisplayScale, PortArrowSize, PortPaddingHorizontal, PortPaddingVertical } from './globals.mjs';
4
4
  import { NumericValue } from './objects/ParamDefinition.mjs';
5
+ import { AllPinTypes, PinTypes } from './objects/PinTypes.mjs';
6
+ import { roundValue } from './utils.mjs';
5
7
  export class Textbox extends Flatten.Polygon {
6
8
  id;
7
9
  text;
@@ -41,32 +43,90 @@ export class Textbox extends Flatten.Polygon {
41
43
  else {
42
44
  throw 'Invalid string passed into textbox';
43
45
  }
44
- const { fontSize = 10, anchor = HorizontalAlign.Left, vanchor = VerticalAlign.Bottom, fontWeight = 'regular', } = style ?? {};
45
- const { width, height, box } = measureTextSize2(useText, defaultFont, fontSize, fontWeight, anchor, vanchor);
46
- const polygonCoords = [
47
- [box.x, box.y],
48
- [box.x2, box.y],
49
- [box.x2, box.y2],
50
- [box.x, box.y2],
51
- [box.x, box.y],
52
- ];
46
+ const { fontSize = 10, anchor = HorizontalAlign.Left, vanchor = VerticalAlign.Bottom, fontWeight = 'regular', portType = null, } = style ?? {};
47
+ const { width, height, box } = measureTextSize2(useText, defaultFont, fontSize * fontDisplayScale, fontWeight, anchor, vanchor);
48
+ let polygonCoords = [];
49
+ let anchorOffsetX = 0;
50
+ let anchorOffsetY = 0;
51
+ if (portType === null) {
52
+ polygonCoords = [
53
+ [box.x, box.y],
54
+ [box.x2, box.y],
55
+ [box.x2, box.y2],
56
+ [box.x, box.y2],
57
+ [box.x, box.y],
58
+ ];
59
+ }
60
+ else if (AllPinTypes.indexOf(portType) !== -1) {
61
+ const paddingHorizontal = PortPaddingHorizontal;
62
+ const paddingVert = PortPaddingVertical;
63
+ if (portType === PinTypes.Input) {
64
+ polygonCoords = [
65
+ [box.x - paddingHorizontal - PortArrowSize, box.y - paddingVert],
66
+ [box.x2 + paddingHorizontal, box.y - paddingVert],
67
+ [box.x2 + paddingHorizontal, box.y2 + paddingVert],
68
+ [box.x - paddingHorizontal - PortArrowSize, box.y2 + paddingVert],
69
+ [box.x - paddingHorizontal - PortArrowSize, box.y - paddingVert],
70
+ ];
71
+ anchorOffsetX += (PortArrowSize + paddingHorizontal);
72
+ }
73
+ else if (portType === PinTypes.Output) {
74
+ polygonCoords = [
75
+ [box.x - paddingHorizontal, box.y - paddingVert],
76
+ [box.x2 + paddingHorizontal + PortArrowSize, box.y - paddingVert],
77
+ [box.x2 + paddingHorizontal + PortArrowSize, box.y2 + paddingVert],
78
+ [box.x - paddingHorizontal, box.y2 + paddingVert],
79
+ [box.x - paddingHorizontal, box.y - paddingVert],
80
+ ];
81
+ anchorOffsetX += paddingHorizontal;
82
+ }
83
+ else if (portType === PinTypes.IO) {
84
+ polygonCoords = [
85
+ [box.x - paddingHorizontal - PortArrowSize, box.y - paddingVert],
86
+ [box.x2 + paddingHorizontal + PortArrowSize, box.y - paddingVert],
87
+ [box.x2 + paddingHorizontal + PortArrowSize, box.y2 + paddingVert],
88
+ [box.x - paddingHorizontal - PortArrowSize, box.y2 + paddingVert],
89
+ [box.x - paddingHorizontal - PortArrowSize, box.y - paddingVert],
90
+ ];
91
+ anchorOffsetX += paddingHorizontal + PortArrowSize;
92
+ }
93
+ else if (portType === PinTypes.Any) {
94
+ polygonCoords = [
95
+ [box.x - paddingHorizontal, box.y - paddingVert],
96
+ [box.x2 + paddingHorizontal, box.y - paddingVert],
97
+ [box.x2 + paddingHorizontal, box.y2 + paddingVert],
98
+ [box.x - paddingHorizontal, box.y2 + paddingVert],
99
+ [box.x - paddingHorizontal, box.y - paddingVert],
100
+ ];
101
+ anchorOffsetX += paddingHorizontal;
102
+ }
103
+ anchorOffsetY += paddingVert / 2;
104
+ }
53
105
  const polygon = new Flatten.Polygon(polygonCoords);
54
- return new Textbox(id, useText, [x, y], polygon, style, box, label);
106
+ return new Textbox(id, useText, [x + anchorOffsetX, y + anchorOffsetY], polygon, style, box, label);
55
107
  }
56
108
  rotate(angle, origin) {
57
109
  const feature = super.rotate(angle, origin);
58
- return new Textbox(this.id, this.text, this.anchorPoint, feature, this.style, this.textMeasurementBounds, this.label);
110
+ const newAnchorPoint = this.transformAnchorPoint(segment => segment.rotate(angle, origin));
111
+ return new Textbox(this.id, this.text, newAnchorPoint, feature, this.style, this.textMeasurementBounds, this.label);
59
112
  }
60
113
  transform(matrix) {
61
114
  const feature = super.transform(matrix);
62
- return new Textbox(this.id, this.text, this.anchorPoint, feature, this.style, this.textMeasurementBounds, this.label);
115
+ const newAnchorPoint = this.transformAnchorPoint(segment => segment.transform(matrix));
116
+ return new Textbox(this.id, this.text, newAnchorPoint, feature, this.style, this.textMeasurementBounds, this.label);
117
+ }
118
+ transformAnchorPoint(callback) {
119
+ const anchorPointSegment = new Flatten.Segment(new Flatten.Point(0, 0), new Flatten.Point(this.anchorPoint));
120
+ const newSegment = callback(anchorPointSegment);
121
+ const lastPoint = newSegment.vertices[newSegment.vertices.length - 1];
122
+ return [
123
+ lastPoint.x, lastPoint.y
124
+ ];
63
125
  }
64
126
  getLabelPosition() {
65
127
  return this.anchorPoint;
66
128
  }
67
129
  }
68
- export class Label extends Textbox {
69
- }
70
130
  export class GeometryProp {
71
131
  name;
72
132
  value;
@@ -150,7 +210,7 @@ export class Geometry {
150
210
  && feature.text.trim().length === 0) {
151
211
  return;
152
212
  }
153
- if (feature instanceof Textbox && !feature.label) {
213
+ if (feature instanceof Textbox) {
154
214
  const [x, y] = feature.anchorPoint;
155
215
  box = {
156
216
  xmin: box.xmin + x,
@@ -247,7 +307,9 @@ export class Geometry {
247
307
  const existingSegments = [];
248
308
  wirePoints.forEach(points => {
249
309
  const tmpPoints = points.map(pt => {
250
- return new Flatten.Point(pt.x, pt.y);
310
+ const roundedX = roundValue(pt.x);
311
+ const roundedY = roundValue(pt.y);
312
+ return new Flatten.Point(roundedX, roundedY);
251
313
  });
252
314
  for (let i = 0; i < tmpPoints.length - 1; i++) {
253
315
  const pt1 = tmpPoints[i];
@@ -23,18 +23,49 @@ export var SymbolPinSide;
23
23
  SymbolPinSide["Left"] = "left";
24
24
  SymbolPinSide["Right"] = "right";
25
25
  })(SymbolPinSide || (SymbolPinSide = {}));
26
+ export var LengthUnit;
27
+ (function (LengthUnit) {
28
+ LengthUnit["mm"] = "mm";
29
+ LengthUnit["mils"] = "mils";
30
+ LengthUnit["px"] = "px";
31
+ })(LengthUnit || (LengthUnit = {}));
32
+ export var WireAutoDirection;
33
+ (function (WireAutoDirection) {
34
+ WireAutoDirection["Auto"] = "auto";
35
+ WireAutoDirection["Auto_"] = "auto_";
36
+ })(WireAutoDirection || (WireAutoDirection = {}));
37
+ export const MilsToMM = 0.0254;
38
+ export const MMToPx = 3.779276;
39
+ export const MMToPt = 2.8346456693;
40
+ export const PxToMM = 0.2645833;
26
41
  export const portWidth = 20;
27
42
  export const portHeight = 2;
28
- export const defaultFont = 'Open Sans-Regular, Arial';
29
- export const defaultFontBold = 'Open Sans-Bold, Arial-Bold, Arial';
43
+ export const defaultGridSizeUnits = MilsToMM * 100;
44
+ export const defaultZoomScale = 2.5;
45
+ export const fontDisplayScale = 0.032;
46
+ export const defaultSymbolLineWidth = MilsToMM * 6;
47
+ export const defaultWireLineWidth = MilsToMM * 6;
48
+ export const defaultPinNameTextSize = 40;
49
+ export const defaultPinIdTextSize = 30;
50
+ export const CustomSymbolPinTextSize = defaultPinNameTextSize;
51
+ export const CustomSymbolPinIdSize = defaultPinIdTextSize;
52
+ export const CustomSymbolRefDesSize = 50;
53
+ export const CustomSymbolParamTextSize = 40;
54
+ export const defaultFrameTitleTextSize = 60;
55
+ export const displayUnits = LengthUnit.mils;
56
+ export const defaultFont = 'Arial';
57
+ export const defaultFontBold = 'Arial';
30
58
  export const defaultFontSize = 10;
31
- export const junctionSize = 5;
59
+ export const junctionSize = MilsToMM * 20;
60
+ export const PortArrowSize = MilsToMM * 50;
61
+ export const PortPaddingHorizontal = MilsToMM * 10;
62
+ export const PortPaddingVertical = MilsToMM * 10;
32
63
  export const ColorScheme = {
33
64
  BodyColor: 'rgb(255, 255, 194)',
34
65
  JunctionColor: 'rgba(0, 132, 0)',
35
66
  WireColor: 'rgb(0, 132, 0)',
36
- PinLineColor: 'rgb(132, 0, 0)',
37
- PinNameColor: 'rgb(0, 132, 132)',
67
+ PinLineColor: '#333',
68
+ PinNameColor: '#333',
38
69
  };
39
70
  export var ComponentTypes;
40
71
  (function (ComponentTypes) {
@@ -1,4 +1,6 @@
1
- import { readFileSync, writeFileSync } from "fs";
1
+ import { readFileSync, writeFileSync, createWriteStream } from "fs";
2
+ import PDFDocument from "pdfkit";
3
+ import SVGtoPDF from "svg-to-pdfkit";
2
4
  import { generateKiCADNetList, printTree } from "./export.mjs";
3
5
  import { LayoutEngine } from "./layout.mjs";
4
6
  import { SequenceAction } from "./objects/ExecutionScope.mjs";
@@ -13,6 +15,7 @@ import { MainLexer } from "./lexer.mjs";
13
15
  import { CircuitScriptParser } from "./antlr/CircuitScriptParser.mjs";
14
16
  import { prepareTokens, SemanticTokensVisitor } from "./SemanticTokenVisitor.mjs";
15
17
  import path from "path";
18
+ import { LengthUnit, MilsToMM, MMToPt, PxToMM } from "./globals.mjs";
16
19
  export var JSModuleType;
17
20
  (function (JSModuleType) {
18
21
  JSModuleType["CommonJs"] = "cjs";
@@ -163,7 +166,10 @@ export function renderScript(scriptData, outputPath, options) {
163
166
  const { tree, parser, hasParseError, hasError, parserTimeTaken, lexerTimeTaken } = parseFileWithVisitor(visitor, scriptData);
164
167
  showStats && console.log('Lexing took:', lexerTimeTaken);
165
168
  showStats && console.log('Parsing took:', parserTimeTaken);
166
- dumpNets && console.log(visitor.dumpNets());
169
+ if (dumpNets) {
170
+ const nets = visitor.dumpNets();
171
+ console.log(nets);
172
+ }
167
173
  dumpData && writeFileSync('dump/tree.lisp', tree.toStringTree(null, parser));
168
174
  dumpData && writeFileSync('dump/raw-parser.txt', visitor.logger.dump());
169
175
  if (hasError || hasParseError) {
@@ -204,17 +210,47 @@ export function renderScript(scriptData, outputPath, options) {
204
210
  dumpData && writeFileSync('dump/raw-sequence.txt', tmpSequence.join('\n'));
205
211
  let svgOutput = null;
206
212
  try {
207
- const layoutEngine = new LayoutEngine();
213
+ let fileExtension = null;
214
+ let showBaseFrame = false;
215
+ if (outputPath) {
216
+ fileExtension = path.extname(outputPath).substring(1);
217
+ if (fileExtension === 'pdf') {
218
+ showBaseFrame = true;
219
+ }
220
+ }
221
+ const layoutEngine = new LayoutEngine({
222
+ showBaseFrame,
223
+ });
208
224
  const layoutTimer = new SimpleStopwatch();
209
225
  const graph = layoutEngine.runLayout(sequence, nets);
210
226
  layoutEngine.printWarnings();
211
227
  showStats && console.log('Layout took:', layoutTimer.lap());
212
228
  dumpData && writeFileSync('dump/raw-layout.txt', layoutEngine.logger.dump());
213
229
  const generateSvgTimer = new SimpleStopwatch();
214
- svgOutput = generateSVG2(graph);
230
+ const { svg: generatedSvg, width: svgWidth, height: svgHeight } = generateSVG2(graph);
231
+ svgOutput = generatedSvg;
215
232
  showStats && console.log('Render took:', generateSvgTimer.lap());
216
233
  if (outputPath) {
217
- writeFileSync(outputPath, svgOutput);
234
+ if (fileExtension === 'svg') {
235
+ writeFileSync(outputPath, svgOutput);
236
+ }
237
+ else if (fileExtension === 'pdf') {
238
+ const doc = new PDFDocument({
239
+ layout: 'landscape',
240
+ size: 'A4',
241
+ });
242
+ const outputStream = createWriteStream(outputPath);
243
+ const paperWidthMM = 297;
244
+ const paperHeightMM = 210;
245
+ const xOffset = (paperWidthMM - svgWidth) / 2;
246
+ const yOffset = (paperHeightMM - svgHeight) / 2;
247
+ SVGtoPDF(doc, svgOutput, xOffset * MMToPt, yOffset * MMToPt);
248
+ doc.pipe(outputStream);
249
+ doc.end();
250
+ }
251
+ else {
252
+ throw "Invalid output format";
253
+ }
218
254
  console.log('Generated file', outputPath);
219
255
  }
220
256
  }
@@ -254,3 +290,36 @@ export function getPackageVersion() {
254
290
  const { version } = packageJson;
255
291
  return version;
256
292
  }
293
+ export class UnitDimension {
294
+ type;
295
+ value;
296
+ constructor(value, type = LengthUnit.mils) {
297
+ this.value = value;
298
+ this.type = type;
299
+ }
300
+ getMM() {
301
+ switch (this.type) {
302
+ case LengthUnit.mm:
303
+ return this.value;
304
+ case LengthUnit.mils:
305
+ return this.value * MilsToMM;
306
+ case LengthUnit.px:
307
+ return this.value * PxToMM;
308
+ }
309
+ }
310
+ static mm(value) {
311
+ return new UnitDimension(value, LengthUnit.mm);
312
+ }
313
+ static mils(value) {
314
+ return new UnitDimension(value, LengthUnit.mils);
315
+ }
316
+ static px(value) {
317
+ return new UnitDimension(value, LengthUnit.px);
318
+ }
319
+ }
320
+ export function milsToMM(value) {
321
+ return value * MilsToMM;
322
+ }
323
+ export function pxToMM(value) {
324
+ return value * PxToMM;
325
+ }