@cj-tech-master/excelts 9.1.0 → 9.2.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/README.md +16 -1
- package/dist/browser/modules/archive/compression/crc32.js +1 -1
- package/dist/browser/modules/archive/crypto/aes.d.ts +0 -8
- package/dist/browser/modules/archive/crypto/aes.js +1 -20
- package/dist/browser/modules/archive/crypto/index.d.ts +2 -1
- package/dist/browser/modules/archive/crypto/index.js +3 -1
- package/dist/browser/modules/csv/parse/row-processor.d.ts +1 -1
- package/dist/browser/modules/csv/worker/worker-script.generated.js +1 -1
- package/dist/browser/modules/excel/utils/cell-matrix.js +1 -0
- package/dist/browser/modules/excel/utils/encryptor.browser.d.ts +4 -5
- package/dist/browser/modules/excel/utils/encryptor.browser.js +7 -12
- package/dist/browser/modules/excel/utils/encryptor.d.ts +1 -1
- package/dist/browser/modules/excel/utils/encryptor.js +4 -7
- package/dist/browser/modules/pdf/builder/document-builder.d.ts +517 -0
- package/dist/browser/modules/pdf/builder/document-builder.js +1493 -0
- package/dist/browser/modules/pdf/builder/form-appearance.d.ts +56 -0
- package/dist/browser/modules/pdf/builder/form-appearance.js +140 -0
- package/dist/browser/modules/pdf/builder/image-utils.d.ts +39 -0
- package/dist/browser/modules/pdf/builder/image-utils.js +129 -0
- package/dist/browser/modules/pdf/builder/pdf-editor.d.ts +230 -0
- package/dist/browser/modules/pdf/builder/pdf-editor.js +1574 -0
- package/dist/browser/modules/pdf/builder/resource-merger.d.ts +41 -0
- package/dist/browser/modules/pdf/builder/resource-merger.js +258 -0
- package/dist/browser/modules/pdf/core/digital-signature.d.ts +109 -0
- package/dist/browser/modules/pdf/core/digital-signature.js +659 -0
- package/dist/browser/modules/pdf/core/encryption.js +8 -7
- package/dist/browser/modules/pdf/core/pdf-object.d.ts +11 -0
- package/dist/browser/modules/pdf/core/pdf-object.js +38 -0
- package/dist/browser/modules/pdf/core/pdf-stream.d.ts +32 -0
- package/dist/browser/modules/pdf/core/pdf-stream.js +66 -0
- package/dist/browser/modules/pdf/core/pdf-writer.d.ts +55 -1
- package/dist/browser/modules/pdf/core/pdf-writer.js +271 -6
- package/dist/browser/modules/pdf/core/pdfa.d.ts +62 -0
- package/dist/browser/modules/pdf/core/pdfa.js +261 -0
- package/dist/browser/modules/pdf/index.d.ts +11 -0
- package/dist/browser/modules/pdf/index.js +9 -0
- package/dist/browser/modules/pdf/reader/bookmark-extractor.d.ts +35 -0
- package/dist/browser/modules/pdf/reader/bookmark-extractor.js +324 -0
- package/dist/browser/modules/pdf/reader/pdf-decrypt.js +6 -5
- package/dist/browser/modules/pdf/reader/pdf-reader.d.ts +17 -0
- package/dist/browser/modules/pdf/reader/pdf-reader.js +26 -2
- package/dist/browser/modules/pdf/reader/table-extractor.d.ts +69 -0
- package/dist/browser/modules/pdf/reader/table-extractor.js +365 -0
- package/dist/browser/modules/pdf/render/layout-engine.d.ts +21 -1
- package/dist/browser/modules/pdf/render/layout-engine.js +112 -5
- package/dist/browser/modules/pdf/render/page-renderer.d.ts +2 -9
- package/dist/browser/modules/pdf/render/page-renderer.js +62 -103
- package/dist/browser/modules/pdf/render/pdf-exporter.js +2 -61
- package/dist/browser/modules/pdf/render/style-converter.d.ts +4 -0
- package/dist/browser/modules/pdf/render/style-converter.js +1 -1
- package/dist/browser/modules/pdf/types.d.ts +14 -1
- package/dist/browser/modules/stream/browser/readable.js +8 -2
- package/dist/browser/utils/crypto.browser.d.ts +64 -0
- package/dist/browser/{modules/pdf/core/crypto.js → utils/crypto.browser.js} +91 -101
- package/dist/browser/utils/crypto.d.ts +97 -0
- package/dist/browser/utils/crypto.js +209 -0
- package/dist/cjs/modules/archive/compression/crc32.js +1 -1
- package/dist/cjs/modules/archive/crypto/aes.js +2 -23
- package/dist/cjs/modules/archive/crypto/index.js +3 -1
- package/dist/cjs/modules/csv/worker/worker-script.generated.js +1 -1
- package/dist/cjs/modules/excel/utils/cell-matrix.js +1 -0
- package/dist/cjs/modules/excel/utils/encryptor.browser.js +7 -12
- package/dist/cjs/modules/excel/utils/encryptor.js +4 -10
- package/dist/cjs/modules/pdf/builder/document-builder.js +1532 -0
- package/dist/cjs/modules/pdf/builder/form-appearance.js +145 -0
- package/dist/cjs/modules/pdf/builder/image-utils.js +135 -0
- package/dist/cjs/modules/pdf/builder/pdf-editor.js +1612 -0
- package/dist/cjs/modules/pdf/builder/resource-merger.js +263 -0
- package/dist/cjs/modules/pdf/core/digital-signature.js +667 -0
- package/dist/cjs/modules/pdf/core/encryption.js +8 -7
- package/dist/cjs/modules/pdf/core/pdf-object.js +38 -0
- package/dist/cjs/modules/pdf/core/pdf-stream.js +66 -0
- package/dist/cjs/modules/pdf/core/pdf-writer.js +272 -6
- package/dist/cjs/modules/pdf/core/pdfa.js +266 -0
- package/dist/cjs/modules/pdf/index.js +19 -1
- package/dist/cjs/modules/pdf/reader/bookmark-extractor.js +327 -0
- package/dist/cjs/modules/pdf/reader/pdf-decrypt.js +6 -5
- package/dist/cjs/modules/pdf/reader/pdf-reader.js +26 -2
- package/dist/cjs/modules/pdf/reader/table-extractor.js +368 -0
- package/dist/cjs/modules/pdf/render/layout-engine.js +113 -4
- package/dist/cjs/modules/pdf/render/page-renderer.js +63 -105
- package/dist/cjs/modules/pdf/render/pdf-exporter.js +3 -62
- package/dist/cjs/modules/pdf/render/style-converter.js +1 -0
- package/dist/cjs/modules/stream/browser/readable.js +8 -2
- package/dist/cjs/{modules/pdf/core/crypto.js → utils/crypto.browser.js} +95 -102
- package/dist/cjs/utils/crypto.js +228 -0
- package/dist/esm/modules/archive/compression/crc32.js +1 -1
- package/dist/esm/modules/archive/crypto/aes.js +1 -20
- package/dist/esm/modules/archive/crypto/index.js +3 -1
- package/dist/esm/modules/csv/worker/worker-script.generated.js +1 -1
- package/dist/esm/modules/excel/utils/cell-matrix.js +1 -0
- package/dist/esm/modules/excel/utils/encryptor.browser.js +7 -12
- package/dist/esm/modules/excel/utils/encryptor.js +4 -7
- package/dist/esm/modules/pdf/builder/document-builder.js +1493 -0
- package/dist/esm/modules/pdf/builder/form-appearance.js +140 -0
- package/dist/esm/modules/pdf/builder/image-utils.js +129 -0
- package/dist/esm/modules/pdf/builder/pdf-editor.js +1574 -0
- package/dist/esm/modules/pdf/builder/resource-merger.js +258 -0
- package/dist/esm/modules/pdf/core/digital-signature.js +659 -0
- package/dist/esm/modules/pdf/core/encryption.js +8 -7
- package/dist/esm/modules/pdf/core/pdf-object.js +38 -0
- package/dist/esm/modules/pdf/core/pdf-stream.js +66 -0
- package/dist/esm/modules/pdf/core/pdf-writer.js +271 -6
- package/dist/esm/modules/pdf/core/pdfa.js +261 -0
- package/dist/esm/modules/pdf/index.js +9 -0
- package/dist/esm/modules/pdf/reader/bookmark-extractor.js +324 -0
- package/dist/esm/modules/pdf/reader/pdf-decrypt.js +6 -5
- package/dist/esm/modules/pdf/reader/pdf-reader.js +26 -2
- package/dist/esm/modules/pdf/reader/table-extractor.js +365 -0
- package/dist/esm/modules/pdf/render/layout-engine.js +112 -5
- package/dist/esm/modules/pdf/render/page-renderer.js +62 -103
- package/dist/esm/modules/pdf/render/pdf-exporter.js +2 -61
- package/dist/esm/modules/pdf/render/style-converter.js +1 -1
- package/dist/esm/modules/stream/browser/readable.js +8 -2
- package/dist/esm/{modules/pdf/core/crypto.js → utils/crypto.browser.js} +91 -101
- package/dist/esm/utils/crypto.js +209 -0
- package/dist/iife/excelts.iife.js +1248 -1074
- package/dist/iife/excelts.iife.js.map +1 -1
- package/dist/iife/excelts.iife.min.js +53 -54
- package/dist/types/modules/archive/crypto/aes.d.ts +0 -8
- package/dist/types/modules/archive/crypto/index.d.ts +2 -1
- package/dist/types/modules/csv/parse/row-processor.d.ts +1 -1
- package/dist/types/modules/excel/utils/encryptor.browser.d.ts +4 -5
- package/dist/types/modules/excel/utils/encryptor.d.ts +1 -1
- package/dist/types/modules/pdf/builder/document-builder.d.ts +517 -0
- package/dist/types/modules/pdf/builder/form-appearance.d.ts +56 -0
- package/dist/types/modules/pdf/builder/image-utils.d.ts +39 -0
- package/dist/types/modules/pdf/builder/pdf-editor.d.ts +230 -0
- package/dist/types/modules/pdf/builder/resource-merger.d.ts +41 -0
- package/dist/types/modules/pdf/core/digital-signature.d.ts +109 -0
- package/dist/types/modules/pdf/core/pdf-object.d.ts +11 -0
- package/dist/types/modules/pdf/core/pdf-stream.d.ts +32 -0
- package/dist/types/modules/pdf/core/pdf-writer.d.ts +55 -1
- package/dist/types/modules/pdf/core/pdfa.d.ts +62 -0
- package/dist/types/modules/pdf/index.d.ts +11 -0
- package/dist/types/modules/pdf/reader/bookmark-extractor.d.ts +35 -0
- package/dist/types/modules/pdf/reader/pdf-reader.d.ts +17 -0
- package/dist/types/modules/pdf/reader/table-extractor.d.ts +69 -0
- package/dist/types/modules/pdf/render/layout-engine.d.ts +21 -1
- package/dist/types/modules/pdf/render/page-renderer.d.ts +2 -9
- package/dist/types/modules/pdf/render/style-converter.d.ts +4 -0
- package/dist/types/modules/pdf/types.d.ts +14 -1
- package/dist/types/utils/crypto.browser.d.ts +64 -0
- package/dist/types/utils/crypto.d.ts +97 -0
- package/package.json +110 -111
- package/dist/browser/modules/pdf/core/crypto.d.ts +0 -65
- package/dist/types/modules/pdf/core/crypto.d.ts +0 -65
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PDF resource dictionary merger — structured merging at the parsed object level.
|
|
3
|
+
*
|
|
4
|
+
* Instead of manipulating serialized PDF dict strings directly, this module
|
|
5
|
+
* parses resource dicts into a structured `PdfResourceDict` (nested Maps),
|
|
6
|
+
* merges them, and serializes back to PDF dict strings.
|
|
7
|
+
*/
|
|
8
|
+
// Known resource categories that contain sub-dicts
|
|
9
|
+
const SUB_DICT_CATEGORIES = new Set([
|
|
10
|
+
"Font",
|
|
11
|
+
"XObject",
|
|
12
|
+
"ExtGState",
|
|
13
|
+
"ColorSpace",
|
|
14
|
+
"Pattern",
|
|
15
|
+
"Shading",
|
|
16
|
+
"Properties"
|
|
17
|
+
]);
|
|
18
|
+
// =============================================================================
|
|
19
|
+
// Merge
|
|
20
|
+
// =============================================================================
|
|
21
|
+
/**
|
|
22
|
+
* Merge two parsed resource dicts. For sub-dict categories, entries are
|
|
23
|
+
* combined; overlay entries win on name collision. For non-sub-dict categories,
|
|
24
|
+
* the overlay value replaces the original.
|
|
25
|
+
*/
|
|
26
|
+
export function mergeResourceDicts(original, overlay) {
|
|
27
|
+
const merged = new Map();
|
|
28
|
+
// Copy all original entries (deep-copy inner maps)
|
|
29
|
+
for (const [category, innerMap] of original) {
|
|
30
|
+
merged.set(category, new Map(innerMap));
|
|
31
|
+
}
|
|
32
|
+
// Merge overlay entries
|
|
33
|
+
for (const [category, overlayInner] of overlay) {
|
|
34
|
+
const existing = merged.get(category);
|
|
35
|
+
if (existing && SUB_DICT_CATEGORIES.has(category)) {
|
|
36
|
+
// Both have this sub-dict category — merge inner entries (overlay wins)
|
|
37
|
+
for (const [name, value] of overlayInner) {
|
|
38
|
+
existing.set(name, value);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
else if (existing && !SUB_DICT_CATEGORIES.has(category)) {
|
|
42
|
+
// Non-sub-dict category — overlay replaces
|
|
43
|
+
merged.set(category, new Map(overlayInner));
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
// Category only in overlay — copy it
|
|
47
|
+
merged.set(category, new Map(overlayInner));
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return merged;
|
|
51
|
+
}
|
|
52
|
+
// =============================================================================
|
|
53
|
+
// Parse
|
|
54
|
+
// =============================================================================
|
|
55
|
+
/**
|
|
56
|
+
* Parse a serialized PDF resource dict string into a structured `PdfResourceDict`.
|
|
57
|
+
*
|
|
58
|
+
* Handles top-level entries like:
|
|
59
|
+
* ```
|
|
60
|
+
* << /Font << /F1 3 0 R /F2 5 0 R >> /XObject << /Im1 7 0 R >> /ProcSet [/PDF /Text] >>
|
|
61
|
+
* ```
|
|
62
|
+
*
|
|
63
|
+
* Sub-dict categories (`Font`, `XObject`, etc.) are parsed into inner name→value maps.
|
|
64
|
+
* Other categories are stored with a single entry keyed by `""`.
|
|
65
|
+
*/
|
|
66
|
+
export function parseResourceDict(dictStr) {
|
|
67
|
+
const result = new Map();
|
|
68
|
+
const trimmed = dictStr.trim();
|
|
69
|
+
if (!trimmed.startsWith("<<") || !trimmed.endsWith(">>")) {
|
|
70
|
+
return result;
|
|
71
|
+
}
|
|
72
|
+
// Strip the outer << >>
|
|
73
|
+
const inner = trimmed.slice(2, -2).trim();
|
|
74
|
+
if (!inner) {
|
|
75
|
+
return result;
|
|
76
|
+
}
|
|
77
|
+
// Parse top-level entries
|
|
78
|
+
const entries = parseDictEntries(inner);
|
|
79
|
+
for (const [key, value] of entries) {
|
|
80
|
+
const valueTrimmed = value.trim();
|
|
81
|
+
if (SUB_DICT_CATEGORIES.has(key) && valueTrimmed.startsWith("<<")) {
|
|
82
|
+
// Parse the sub-dict into name→value pairs
|
|
83
|
+
const subInner = valueTrimmed.slice(2, -2).trim();
|
|
84
|
+
const subEntries = parseDictEntries(subInner);
|
|
85
|
+
const innerMap = new Map();
|
|
86
|
+
for (const [subKey, subValue] of subEntries) {
|
|
87
|
+
innerMap.set(subKey, subValue.trim());
|
|
88
|
+
}
|
|
89
|
+
result.set(key, innerMap);
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
// Non-sub-dict category — store with empty key
|
|
93
|
+
const innerMap = new Map();
|
|
94
|
+
innerMap.set("", valueTrimmed);
|
|
95
|
+
result.set(key, innerMap);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return result;
|
|
99
|
+
}
|
|
100
|
+
// =============================================================================
|
|
101
|
+
// Serialize
|
|
102
|
+
// =============================================================================
|
|
103
|
+
/**
|
|
104
|
+
* Serialize a structured `PdfResourceDict` back to a PDF dict string.
|
|
105
|
+
*/
|
|
106
|
+
export function serializeResourceDict(dict) {
|
|
107
|
+
if (dict.size === 0) {
|
|
108
|
+
return "<< >>";
|
|
109
|
+
}
|
|
110
|
+
const parts = ["<<"];
|
|
111
|
+
for (const [category, innerMap] of dict) {
|
|
112
|
+
if (SUB_DICT_CATEGORIES.has(category) && !(innerMap.size === 1 && innerMap.has(""))) {
|
|
113
|
+
// Sub-dict category — serialize as << /Name value ... >>
|
|
114
|
+
const subParts = ["<<"];
|
|
115
|
+
for (const [name, value] of innerMap) {
|
|
116
|
+
subParts.push(`/${name} ${value}`);
|
|
117
|
+
}
|
|
118
|
+
subParts.push(">>");
|
|
119
|
+
parts.push(`/${category} ${subParts.join(" ")}`);
|
|
120
|
+
}
|
|
121
|
+
else {
|
|
122
|
+
// Non-sub-dict or single-value category
|
|
123
|
+
const value = innerMap.get("") ?? "";
|
|
124
|
+
if (value) {
|
|
125
|
+
parts.push(`/${category} ${value}`);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
parts.push(">>");
|
|
130
|
+
return parts.join(" ");
|
|
131
|
+
}
|
|
132
|
+
// =============================================================================
|
|
133
|
+
// Internal: Dict Entry Parser
|
|
134
|
+
// =============================================================================
|
|
135
|
+
/**
|
|
136
|
+
* Parse the inner content of a `<< ... >>` dict (with outer delimiters already
|
|
137
|
+
* stripped) into key→value pairs. Handles nested `<< >>` and `[ ]` by depth
|
|
138
|
+
* counting.
|
|
139
|
+
*/
|
|
140
|
+
function parseDictEntries(inner) {
|
|
141
|
+
const entries = new Map();
|
|
142
|
+
let i = 0;
|
|
143
|
+
while (i < inner.length) {
|
|
144
|
+
// Skip whitespace
|
|
145
|
+
while (i < inner.length && isWhitespace(inner[i])) {
|
|
146
|
+
i++;
|
|
147
|
+
}
|
|
148
|
+
if (i >= inner.length) {
|
|
149
|
+
break;
|
|
150
|
+
}
|
|
151
|
+
// Expect a name: /KeyName
|
|
152
|
+
if (inner[i] !== "/") {
|
|
153
|
+
break; // malformed — bail
|
|
154
|
+
}
|
|
155
|
+
i++; // skip '/'
|
|
156
|
+
const nameStart = i;
|
|
157
|
+
while (i < inner.length && !isDelimiterOrWhitespace(inner[i])) {
|
|
158
|
+
i++;
|
|
159
|
+
}
|
|
160
|
+
const key = inner.slice(nameStart, i);
|
|
161
|
+
// Skip whitespace
|
|
162
|
+
while (i < inner.length && isWhitespace(inner[i])) {
|
|
163
|
+
i++;
|
|
164
|
+
}
|
|
165
|
+
// Read the value
|
|
166
|
+
const valueStart = i;
|
|
167
|
+
if (i < inner.length && inner[i] === "<" && i + 1 < inner.length && inner[i + 1] === "<") {
|
|
168
|
+
// Sub-dict: count nested << >>
|
|
169
|
+
i = skipNestedDict(inner, i);
|
|
170
|
+
}
|
|
171
|
+
else if (i < inner.length && inner[i] === "[") {
|
|
172
|
+
// Array: find matching ]
|
|
173
|
+
i = skipNestedArray(inner, i);
|
|
174
|
+
}
|
|
175
|
+
else {
|
|
176
|
+
// Token(s) — read until next top-level '/' or end
|
|
177
|
+
i = skipTokenValue(inner, i, valueStart);
|
|
178
|
+
}
|
|
179
|
+
const value = inner.slice(valueStart, i).trim();
|
|
180
|
+
if (key && value) {
|
|
181
|
+
entries.set(key, value);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
return entries;
|
|
185
|
+
}
|
|
186
|
+
function isWhitespace(ch) {
|
|
187
|
+
return ch === " " || ch === "\t" || ch === "\n" || ch === "\r" || ch === "\f";
|
|
188
|
+
}
|
|
189
|
+
function isDelimiterOrWhitespace(ch) {
|
|
190
|
+
return (isWhitespace(ch) ||
|
|
191
|
+
ch === "/" ||
|
|
192
|
+
ch === "<" ||
|
|
193
|
+
ch === ">" ||
|
|
194
|
+
ch === "[" ||
|
|
195
|
+
ch === "]" ||
|
|
196
|
+
ch === "(" ||
|
|
197
|
+
ch === ")");
|
|
198
|
+
}
|
|
199
|
+
/** Skip over a `<< ... >>` block, handling nesting. Returns position after `>>`. */
|
|
200
|
+
function skipNestedDict(str, start) {
|
|
201
|
+
let i = start;
|
|
202
|
+
let depth = 0;
|
|
203
|
+
while (i < str.length) {
|
|
204
|
+
if (str[i] === "<" && i + 1 < str.length && str[i + 1] === "<") {
|
|
205
|
+
depth++;
|
|
206
|
+
i += 2;
|
|
207
|
+
}
|
|
208
|
+
else if (str[i] === ">" && i + 1 < str.length && str[i + 1] === ">") {
|
|
209
|
+
depth--;
|
|
210
|
+
i += 2;
|
|
211
|
+
if (depth === 0) {
|
|
212
|
+
return i;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
else {
|
|
216
|
+
i++;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
return i;
|
|
220
|
+
}
|
|
221
|
+
/** Skip over a `[ ... ]` block, handling nesting. Returns position after `]`. */
|
|
222
|
+
function skipNestedArray(str, start) {
|
|
223
|
+
let i = start;
|
|
224
|
+
let depth = 0;
|
|
225
|
+
while (i < str.length) {
|
|
226
|
+
if (str[i] === "[") {
|
|
227
|
+
depth++;
|
|
228
|
+
}
|
|
229
|
+
else if (str[i] === "]") {
|
|
230
|
+
depth--;
|
|
231
|
+
if (depth === 0) {
|
|
232
|
+
i++;
|
|
233
|
+
return i;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
i++;
|
|
237
|
+
}
|
|
238
|
+
return i;
|
|
239
|
+
}
|
|
240
|
+
/**
|
|
241
|
+
* Skip a token value (ref, name, number, etc.) that is not a dict or array.
|
|
242
|
+
* Stops at the next top-level `/` preceded by whitespace, or at a `<<` pair.
|
|
243
|
+
*/
|
|
244
|
+
function skipTokenValue(str, i, valueStart) {
|
|
245
|
+
while (i < str.length) {
|
|
246
|
+
if (str[i] === "/" && i > valueStart) {
|
|
247
|
+
// Check if this '/' starts a new key (preceded by whitespace)
|
|
248
|
+
if (isWhitespace(str[i - 1])) {
|
|
249
|
+
break;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
if (str[i] === "<" && i + 1 < str.length && str[i + 1] === "<") {
|
|
253
|
+
break; // shouldn't happen at this level, but be safe
|
|
254
|
+
}
|
|
255
|
+
i++;
|
|
256
|
+
}
|
|
257
|
+
return i;
|
|
258
|
+
}
|