reamkit 1.15.4 → 1.17.0
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/esm/core/bidi/segments.js +1 -2
- package/dist/esm/core/converter/facade.d.ts +5 -0
- package/dist/esm/core/converter/project.js +5 -1
- package/dist/esm/core/converter/ream.d.ts +6 -0
- package/dist/esm/core/converter/ream.js +34 -8
- package/dist/esm/core/document-model/index.d.ts +1 -1
- package/dist/esm/core/document-model/types.d.ts +659 -7
- package/dist/esm/core/drawingml/chart-geometry.d.ts +34 -10
- package/dist/esm/core/drawingml/chart-geometry.js +424 -92
- package/dist/esm/core/drawingml/chart-parser.js +301 -11
- package/dist/esm/core/drawingml/colors.d.ts +7 -7
- package/dist/esm/core/drawingml/colors.js +84 -18
- package/dist/esm/core/drawingml/preset-geometry.js +240 -6
- package/dist/esm/core/drawingml/shape-render.d.ts +12 -0
- package/dist/esm/core/drawingml/shape-render.js +116 -3
- package/dist/esm/core/drawingml/sparkline-geometry.js +6 -4
- package/dist/esm/core/drawingml/theme-parser.d.ts +51 -0
- package/dist/esm/core/drawingml/theme-parser.js +92 -1
- package/dist/esm/core/font/measure.d.ts +5 -0
- package/dist/esm/core/font/measure.js +21 -1
- package/dist/esm/core/font/ttf-subset.js +26 -1
- package/dist/esm/core/images.d.ts +2 -2
- package/dist/esm/core/images.js +353 -43
- package/dist/esm/core/indexed-colors.d.ts +11 -0
- package/dist/esm/core/indexed-colors.js +81 -0
- package/dist/esm/core/ir/flow.d.ts +23 -1
- package/dist/esm/core/ir/sheet.d.ts +61 -0
- package/dist/esm/{excel → core}/number-format.d.ts +37 -0
- package/dist/esm/core/number-format.js +800 -0
- package/dist/esm/core/numbering/apply.d.ts +5 -3
- package/dist/esm/core/numbering/apply.js +42 -11
- package/dist/esm/core/numbering/index.js +2 -0
- package/dist/esm/core/numbering/state.d.ts +13 -1
- package/dist/esm/core/numbering/state.js +256 -15
- package/dist/esm/core/opc/alternate-content.d.ts +13 -0
- package/dist/esm/core/opc/alternate-content.js +72 -0
- package/dist/esm/core/opc/core-properties.js +1 -0
- package/dist/esm/core/opc/package.d.ts +14 -1
- package/dist/esm/core/opc/package.js +43 -4
- package/dist/esm/core/opc/relationships.js +1 -0
- package/dist/esm/core/opc/xml-entities.d.ts +12 -0
- package/dist/esm/core/opc/xml-entities.js +91 -0
- package/dist/esm/core/po-helpers.js +16 -1
- package/dist/esm/core/spreadsheet-model/index.d.ts +1 -1
- package/dist/esm/core/spreadsheet-model/types.d.ts +137 -0
- package/dist/esm/core/style-cascade/resolver.js +66 -11
- package/dist/esm/core/style-cascade/table.js +35 -11
- package/dist/esm/core/style-cascade/types.d.ts +42 -1
- package/dist/esm/core/style-cascade/types.js +4 -0
- package/dist/esm/core/vector.d.ts +30 -0
- package/dist/esm/excel/activex-parser.js +1 -0
- package/dist/esm/excel/column-bands.d.ts +26 -1
- package/dist/esm/excel/column-bands.js +156 -20
- package/dist/esm/excel/comments-parser.js +5 -3
- package/dist/esm/excel/conditional-format.d.ts +25 -2
- package/dist/esm/excel/conditional-format.js +63 -19
- package/dist/esm/excel/escaped-text.d.ts +9 -0
- package/dist/esm/excel/escaped-text.js +20 -0
- package/dist/esm/excel/form-control-parser.js +1 -0
- package/dist/esm/excel/formula/dates.js +1 -1
- package/dist/esm/excel/header-footer.d.ts +9 -1
- package/dist/esm/excel/header-footer.js +162 -14
- package/dist/esm/excel/index.d.ts +1 -1
- package/dist/esm/excel/index.js +2 -2
- package/dist/esm/excel/pivot-table-parser.js +1 -0
- package/dist/esm/excel/print-model.d.ts +75 -0
- package/dist/esm/excel/print-model.js +899 -77
- package/dist/esm/excel/printer-settings.d.ts +16 -0
- package/dist/esm/excel/printer-settings.js +36 -0
- package/dist/esm/excel/rich-value.d.ts +13 -0
- package/dist/esm/excel/rich-value.js +120 -0
- package/dist/esm/excel/shared-strings-parser.js +11 -7
- package/dist/esm/excel/sheet-drawing.d.ts +11 -0
- package/dist/esm/excel/sheet-drawing.js +26 -3
- package/dist/esm/excel/sheet-shape-parser.d.ts +53 -1
- package/dist/esm/excel/sheet-shape-parser.js +461 -32
- package/dist/esm/excel/sheet-to-flow.d.ts +23 -2
- package/dist/esm/excel/sheet-to-flow.js +553 -36
- package/dist/esm/excel/slicer-parser.js +1 -0
- package/dist/esm/excel/styles-parser.d.ts +30 -2
- package/dist/esm/excel/styles-parser.js +177 -40
- package/dist/esm/excel/table-parser.js +1 -0
- package/dist/esm/excel/vml-drawing.d.ts +68 -0
- package/dist/esm/excel/vml-drawing.js +158 -0
- package/dist/esm/excel/workbook-parser.d.ts +5 -0
- package/dist/esm/excel/workbook-parser.js +5 -1
- package/dist/esm/excel/worksheet-parser.d.ts +2 -1
- package/dist/esm/excel/worksheet-parser.js +150 -61
- package/dist/esm/excel/xlsx-reader.js +242 -11
- package/dist/esm/excel/xlsx-to-pdf.js +12 -1
- package/dist/esm/html/html-writer.js +29 -6
- package/dist/esm/layout/page-doc.d.ts +87 -1
- package/dist/esm/layout/styled-layout.d.ts +46 -2
- package/dist/esm/layout/styled-layout.js +2345 -274
- package/dist/esm/pdf/cid-font.js +20 -9
- package/dist/esm/pdf/objects.d.ts +13 -0
- package/dist/esm/pdf/objects.js +18 -1
- package/dist/esm/pdf/shading.js +76 -6
- package/dist/esm/pdf/styled-page-emitter.js +355 -49
- package/dist/esm/pdf/text-page-renderer.js +1 -0
- package/dist/esm/pdf/vector-graphics.d.ts +31 -2
- package/dist/esm/pdf/vector-graphics.js +103 -3
- package/dist/esm/pptx/pptx-reader.js +2 -0
- package/dist/esm/pptx/slide-parser.d.ts +21 -4
- package/dist/esm/pptx/slide-parser.js +98 -24
- package/dist/esm/svg/svg-writer.js +23 -4
- package/dist/esm/word/document-parser.d.ts +96 -7
- package/dist/esm/word/document-parser.js +538 -116
- package/dist/esm/word/docx-reader.js +135 -43
- package/dist/esm/word/docx-writer.js +6 -1
- package/dist/esm/word/drawing-parser.d.ts +86 -5
- package/dist/esm/word/drawing-parser.js +1590 -54
- package/dist/esm/word/index.d.ts +4 -4
- package/dist/esm/word/numbering-parser.d.ts +2 -1
- package/dist/esm/word/numbering-parser.js +108 -21
- package/dist/esm/word/paragraph-properties.d.ts +11 -1
- package/dist/esm/word/paragraph-properties.js +219 -10
- package/dist/esm/word/run-properties.js +45 -1
- package/dist/esm/word/settings-parser.d.ts +24 -0
- package/dist/esm/word/settings-parser.js +32 -5
- package/dist/esm/word/shading.d.ts +11 -0
- package/dist/esm/word/shading.js +64 -0
- package/dist/esm/word/styles-parser.js +9 -2
- package/dist/esm/word/table-parser.d.ts +9 -1
- package/dist/esm/word/table-parser.js +182 -30
- package/package.json +7 -3
- package/dist/esm/excel/number-format.js +0 -476
package/dist/esm/pdf/cid-font.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { glyphClosure, subsetTtf } from "../core/font/ttf-subset.js";
|
|
2
2
|
import { createFontMeasure } from "../core/font/measure.js";
|
|
3
|
-
import { dict, name, ref
|
|
3
|
+
import { deflatedStream, dict, name, ref } from "./objects.js";
|
|
4
4
|
//#region src/pdf/cid-font.ts
|
|
5
5
|
var encoder = new TextEncoder();
|
|
6
6
|
/**
|
|
@@ -17,9 +17,10 @@ function embedTtfFont(doc, parsed, options = {}) {
|
|
|
17
17
|
const scale = 1e3 / parsed.unitsPerEm;
|
|
18
18
|
const toPdfUnits = (v) => Math.round(v * scale);
|
|
19
19
|
const usedGidsArr = options.usedGids ? [...options.usedGids] : void 0;
|
|
20
|
+
const subsetGids = usedGidsArr ? [...glyphClosure(parsed, usedGidsArr)].sort((a, b) => a - b) : void 0;
|
|
20
21
|
const fontFileBytes = usedGidsArr ? subsetTtf(parsed, usedGidsArr) : parsed.raw;
|
|
21
|
-
const fontFileRef = doc.add(
|
|
22
|
-
const cidSetRef = usedGidsArr !== void 0 && options.cidSet ? doc.add(
|
|
22
|
+
const fontFileRef = doc.add(deflatedStream({ Length1: fontFileBytes.byteLength }, fontFileBytes));
|
|
23
|
+
const cidSetRef = usedGidsArr !== void 0 && options.cidSet ? doc.add(deflatedStream({}, buildCidSet(glyphClosure(parsed, usedGidsArr)))) : void 0;
|
|
23
24
|
const baseFontName = usedGidsArr ? `${subsetTag(parsed.postScriptName, usedGidsArr)}+${parsed.postScriptName}` : parsed.postScriptName;
|
|
24
25
|
const descriptorRef = doc.add(dict({
|
|
25
26
|
Type: name("FontDescriptor"),
|
|
@@ -40,8 +41,7 @@ function embedTtfFont(doc, parsed, options = {}) {
|
|
|
40
41
|
FontFile2: ref(fontFileRef.id),
|
|
41
42
|
...cidSetRef ? { CIDSet: ref(cidSetRef.id) } : {}
|
|
42
43
|
}));
|
|
43
|
-
const
|
|
44
|
-
for (let i = 0; i < parsed.numGlyphs; i++) widths[i] = toPdfUnits(parsed.advanceWidths[i] ?? 0);
|
|
44
|
+
const widthRuns = consecutiveRuns(subsetGids ?? Array.from({ length: parsed.numGlyphs }, (_, i) => i)).map((run) => [run[0], run.map((g) => toPdfUnits(parsed.advanceWidths[g] ?? 0))]);
|
|
45
45
|
const cidFontRef = doc.add(dict({
|
|
46
46
|
Type: name("Font"),
|
|
47
47
|
Subtype: name("CIDFontType2"),
|
|
@@ -53,10 +53,10 @@ function embedTtfFont(doc, parsed, options = {}) {
|
|
|
53
53
|
}),
|
|
54
54
|
FontDescriptor: ref(descriptorRef.id),
|
|
55
55
|
DW: 1e3,
|
|
56
|
-
W: [
|
|
56
|
+
W: widthRuns.flatMap(([first, ws]) => [first, ws]),
|
|
57
57
|
CIDToGIDMap: name("Identity")
|
|
58
58
|
}));
|
|
59
|
-
const toUnicodeRef = doc.add(
|
|
59
|
+
const toUnicodeRef = doc.add(deflatedStream({}, buildToUnicodeCMap(parsed, subsetGids)));
|
|
60
60
|
const type0Ref = doc.add(dict({
|
|
61
61
|
Type: name("Font"),
|
|
62
62
|
Subtype: name("Type0"),
|
|
@@ -99,7 +99,18 @@ function subsetTag(postScriptName, gids) {
|
|
|
99
99
|
}
|
|
100
100
|
return tag;
|
|
101
101
|
}
|
|
102
|
-
|
|
102
|
+
/** Split a sorted id list into runs of consecutive ids. */
|
|
103
|
+
function consecutiveRuns(ids) {
|
|
104
|
+
const runs = [];
|
|
105
|
+
for (const id of ids) {
|
|
106
|
+
const last = runs[runs.length - 1];
|
|
107
|
+
if (last && id === last[last.length - 1] + 1) last.push(id);
|
|
108
|
+
else runs.push([id]);
|
|
109
|
+
}
|
|
110
|
+
return runs;
|
|
111
|
+
}
|
|
112
|
+
function buildToUnicodeCMap(parsed, subsetGids) {
|
|
113
|
+
const inSubset = subsetGids ? new Set(subsetGids) : void 0;
|
|
103
114
|
const gidToCps = /* @__PURE__ */ new Map();
|
|
104
115
|
const order = [];
|
|
105
116
|
for (let cp = 9; cp <= 65535; cp++) {
|
|
@@ -131,7 +142,7 @@ function buildToUnicodeCMap(parsed) {
|
|
|
131
142
|
}
|
|
132
143
|
}
|
|
133
144
|
}
|
|
134
|
-
const pairs = order.map((g) => [g, gidToCps.get(g)]).sort((a, b) => a[0] - b[0]);
|
|
145
|
+
const pairs = order.filter((g) => inSubset === void 0 || inSubset.has(g)).map((g) => [g, gidToCps.get(g)]).sort((a, b) => a[0] - b[0]);
|
|
135
146
|
const utf16beHex = (cps) => {
|
|
136
147
|
let hex = "";
|
|
137
148
|
for (const cp of cps) if (cp <= 65535) hex += cp.toString(16).padStart(4, "0").toUpperCase();
|
|
@@ -62,6 +62,19 @@ export declare const ref: (id: number, gen?: number) => PdfRef;
|
|
|
62
62
|
export declare const dict: (entries: Record<string, PdfValue>) => PdfDict;
|
|
63
63
|
/** Construct a {@link PdfStream} from a dictionary literal plus its byte payload. */
|
|
64
64
|
export declare const stream: (entries: Record<string, PdfValue>, data: Uint8Array) => PdfStream;
|
|
65
|
+
/**
|
|
66
|
+
* A {@link PdfStream} whose payload is deflated (ISO 32000-1 §7.4.4).
|
|
67
|
+
*
|
|
68
|
+
* Nothing we wrote was compressed at all — `grep /Filter` over one of our own
|
|
69
|
+
* files returned the empty set — so a three-page workbook came out at 192 KB
|
|
70
|
+
* against LibreOffice's 21 KB, most of it an uncompressed font program. Any
|
|
71
|
+
* stream that is not already in a compressed image format belongs here.
|
|
72
|
+
*
|
|
73
|
+
* @param entries The stream's dictionary, minus `/Filter` and `/Length`.
|
|
74
|
+
* @param data The raw payload.
|
|
75
|
+
* @returns The stream, deflated, with `/Filter /FlateDecode`.
|
|
76
|
+
*/
|
|
77
|
+
export declare const deflatedStream: (entries: Record<string, PdfValue>, data: Uint8Array) => PdfStream;
|
|
65
78
|
/**
|
|
66
79
|
* Build a PDF Unicode string (UTF-16BE with BOM) suitable for `/Info` or
|
|
67
80
|
* document-level text metadata. Accepts arbitrary Unicode input.
|
package/dist/esm/pdf/objects.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { zlibSync } from "fflate";
|
|
1
2
|
//#region src/pdf/objects.ts
|
|
2
3
|
/** The PDF `null` object (§7.3.9), modelled as a unique symbol. */
|
|
3
4
|
var PDF_NULL = Symbol("PDF_NULL");
|
|
@@ -61,6 +62,22 @@ var dict = (entries) => new Map(Object.entries(entries));
|
|
|
61
62
|
/** Construct a {@link PdfStream} from a dictionary literal plus its byte payload. */
|
|
62
63
|
var stream = (entries, data) => new PdfStream(dict(entries), data);
|
|
63
64
|
/**
|
|
65
|
+
* A {@link PdfStream} whose payload is deflated (ISO 32000-1 §7.4.4).
|
|
66
|
+
*
|
|
67
|
+
* Nothing we wrote was compressed at all — `grep /Filter` over one of our own
|
|
68
|
+
* files returned the empty set — so a three-page workbook came out at 192 KB
|
|
69
|
+
* against LibreOffice's 21 KB, most of it an uncompressed font program. Any
|
|
70
|
+
* stream that is not already in a compressed image format belongs here.
|
|
71
|
+
*
|
|
72
|
+
* @param entries The stream's dictionary, minus `/Filter` and `/Length`.
|
|
73
|
+
* @param data The raw payload.
|
|
74
|
+
* @returns The stream, deflated, with `/Filter /FlateDecode`.
|
|
75
|
+
*/
|
|
76
|
+
var deflatedStream = (entries, data) => new PdfStream(dict({
|
|
77
|
+
...entries,
|
|
78
|
+
Filter: name("FlateDecode")
|
|
79
|
+
}), zlibSync(data));
|
|
80
|
+
/**
|
|
64
81
|
* Build a PDF Unicode string (UTF-16BE with BOM) suitable for `/Info` or
|
|
65
82
|
* document-level text metadata. Accepts arbitrary Unicode input.
|
|
66
83
|
*
|
|
@@ -87,4 +104,4 @@ var unicodeString = (s) => {
|
|
|
87
104
|
return new PdfHexString(out);
|
|
88
105
|
};
|
|
89
106
|
//#endregion
|
|
90
|
-
export { PDF_NULL, PdfHexString, PdfName, PdfRawToken, PdfRef, PdfStream, dict, name, ref, stream, unicodeString };
|
|
107
|
+
export { PDF_NULL, PdfHexString, PdfName, PdfRawToken, PdfRef, PdfStream, deflatedStream, dict, name, ref, stream, unicodeString };
|
package/dist/esm/pdf/shading.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { dict, name, ref } from "./objects.js";
|
|
1
|
+
import { dict, name, ref, stream } from "./objects.js";
|
|
2
2
|
//#region src/pdf/shading.ts
|
|
3
3
|
/**
|
|
4
4
|
* The local-space bounding box of a shape's paths (y-up).
|
|
@@ -41,8 +41,7 @@ function shapeBbox(shape) {
|
|
|
41
41
|
* @returns A reference to the `/Pattern` object, for a `/Pattern cs /Pn scn` fill.
|
|
42
42
|
*/
|
|
43
43
|
function buildGradientPattern(doc, gradient, bbox, ctm) {
|
|
44
|
-
const
|
|
45
|
-
const shading = gradient.kind === "radial" ? radialShading(bbox, fnRef) : axialShading(gradient.angle ?? 0, bbox, fnRef);
|
|
44
|
+
const shading = gradient.kind === "radial" && gradient.sweep === "rect" ? rectShading(doc, bbox, gradient.stops, gradient.center) : gradient.kind === "radial" ? radialShading(bbox, doc.add(buildRamp(gradient.stops)), gradient.center) : axialShading(gradient.angle ?? 0, bbox, doc.add(buildRamp(gradient.stops)));
|
|
46
45
|
return doc.add(dict({
|
|
47
46
|
Type: name("Pattern"),
|
|
48
47
|
PatternType: 2,
|
|
@@ -82,9 +81,9 @@ function axialShading(angleDeg, b, fnRef) {
|
|
|
82
81
|
Extend: [true, true]
|
|
83
82
|
});
|
|
84
83
|
}
|
|
85
|
-
function radialShading(b, fnRef) {
|
|
86
|
-
const cx = (b.minX + b.maxX) / 2;
|
|
87
|
-
const cy = (b.minY + b.maxY) / 2;
|
|
84
|
+
function radialShading(b, fnRef, center) {
|
|
85
|
+
const cx = center ? b.minX + center.x * (b.maxX - b.minX) : (b.minX + b.maxX) / 2;
|
|
86
|
+
const cy = center ? b.maxY - center.y * (b.maxY - b.minY) : (b.minY + b.maxY) / 2;
|
|
88
87
|
const r = Math.hypot(b.maxX - b.minX, b.maxY - b.minY) / 2;
|
|
89
88
|
return dict({
|
|
90
89
|
ShadingType: 3,
|
|
@@ -101,6 +100,77 @@ function radialShading(b, fnRef) {
|
|
|
101
100
|
Extend: [true, true]
|
|
102
101
|
});
|
|
103
102
|
}
|
|
103
|
+
function rectShading(doc, b, stopsIn, center) {
|
|
104
|
+
const cx = center ? b.minX + center.x * (b.maxX - b.minX) : (b.minX + b.maxX) / 2;
|
|
105
|
+
const cy = center ? b.maxY - center.y * (b.maxY - b.minY) : (b.minY + b.maxY) / 2;
|
|
106
|
+
const halfW = Math.max((b.maxX - b.minX) / 2, 1e-6);
|
|
107
|
+
const halfH = Math.max((b.maxY - b.minY) / 2, 1e-6);
|
|
108
|
+
const domain = [
|
|
109
|
+
b.minX,
|
|
110
|
+
b.maxX,
|
|
111
|
+
b.minY,
|
|
112
|
+
b.maxY
|
|
113
|
+
];
|
|
114
|
+
const fn = doc.add(stream({
|
|
115
|
+
FunctionType: 4,
|
|
116
|
+
Domain: domain,
|
|
117
|
+
Range: [
|
|
118
|
+
0,
|
|
119
|
+
1,
|
|
120
|
+
0,
|
|
121
|
+
1,
|
|
122
|
+
0,
|
|
123
|
+
1
|
|
124
|
+
]
|
|
125
|
+
}, new TextEncoder().encode(rectSweepProgram(cx, cy, 1 / halfW, 1 / halfH, stopsIn))));
|
|
126
|
+
return dict({
|
|
127
|
+
ShadingType: 1,
|
|
128
|
+
ColorSpace: name("DeviceRGB"),
|
|
129
|
+
Domain: domain,
|
|
130
|
+
Function: ref(fn.id)
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
function rectSweepProgram(cx, cy, sx, sy, stopsIn) {
|
|
134
|
+
const stops = normalizeStops(stopsIn);
|
|
135
|
+
return [
|
|
136
|
+
"{",
|
|
137
|
+
`${ps(cy)} sub abs ${ps(sy)} mul`,
|
|
138
|
+
`exch ${ps(cx)} sub abs ${ps(sx)} mul`,
|
|
139
|
+
"2 copy lt { exch } if pop",
|
|
140
|
+
"dup 1 gt { pop 1 } if",
|
|
141
|
+
rampProgram(stops, 0),
|
|
142
|
+
"}"
|
|
143
|
+
].join("\n");
|
|
144
|
+
}
|
|
145
|
+
function rampProgram(stops, i) {
|
|
146
|
+
const last = i >= stops.length - 2;
|
|
147
|
+
const a = stops[i];
|
|
148
|
+
const b = stops[i + 1];
|
|
149
|
+
const seg = segmentProgram(a, b);
|
|
150
|
+
if (last) return seg;
|
|
151
|
+
return `dup ${ps(b.offset)} le { ${seg} } { ${rampProgram(stops, i + 1)} } ifelse`;
|
|
152
|
+
}
|
|
153
|
+
function segmentProgram(a, b) {
|
|
154
|
+
const c0 = rgb01(a.colorHex);
|
|
155
|
+
const c1 = rgb01(b.colorHex);
|
|
156
|
+
const span = b.offset - a.offset;
|
|
157
|
+
if (span <= 1e-9) return `pop ${c1.map(ps).join(" ")}`;
|
|
158
|
+
return [
|
|
159
|
+
`${ps(a.offset)} sub ${ps(1 / span)} mul`,
|
|
160
|
+
"dup 0 lt { pop 0 } if dup 1 gt { pop 1 } if",
|
|
161
|
+
"dup dup",
|
|
162
|
+
`${ps(c1[0] - c0[0])} mul ${ps(c0[0])} add`,
|
|
163
|
+
"3 1 roll",
|
|
164
|
+
`${ps(c1[1] - c0[1])} mul ${ps(c0[1])} add`,
|
|
165
|
+
"exch",
|
|
166
|
+
`${ps(c1[2] - c0[2])} mul ${ps(c0[2])} add`
|
|
167
|
+
].join(" ");
|
|
168
|
+
}
|
|
169
|
+
function ps(n) {
|
|
170
|
+
if (!Number.isFinite(n)) return "0";
|
|
171
|
+
const s = n.toFixed(6).replace(/0+$/u, "").replace(/\.$/u, "");
|
|
172
|
+
return s === "" || s === "-0" ? "0" : s;
|
|
173
|
+
}
|
|
104
174
|
function buildRamp(stopsIn) {
|
|
105
175
|
const stops = normalizeStops(stopsIn);
|
|
106
176
|
if (stops.length <= 2) return dict({
|