@embedpdf/plugin-annotation 2.7.0 → 2.9.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/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +1131 -203
- package/dist/index.js.map +1 -1
- package/dist/lib/annotation-plugin.d.ts +2 -0
- package/dist/lib/geometry/cloudy-border.d.ts +90 -0
- package/dist/lib/geometry/index.d.ts +1 -0
- package/dist/lib/handlers/index.d.ts +4 -0
- package/dist/lib/handlers/insert-text.handler.d.ts +8 -0
- package/dist/lib/handlers/replace-text.handler.d.ts +9 -0
- package/dist/lib/handlers/selection-utils.d.ts +7 -0
- package/dist/lib/handlers/text-markup.handler.d.ts +7 -0
- package/dist/lib/handlers/text.handler.d.ts +3 -0
- package/dist/lib/handlers/types.d.ts +14 -1
- package/dist/lib/helpers.d.ts +2 -1
- package/dist/lib/selectors.d.ts +6 -1
- package/dist/lib/tools/default-tools.d.ts +141 -45
- package/dist/lib/tools/tools-utils.d.ts +2 -0
- package/dist/lib/tools/types.d.ts +34 -1
- package/dist/lib/types.d.ts +1 -0
- package/dist/preact/index.cjs +1 -1
- package/dist/preact/index.cjs.map +1 -1
- package/dist/preact/index.js +621 -274
- package/dist/preact/index.js.map +1 -1
- package/dist/react/index.cjs +1 -1
- package/dist/react/index.cjs.map +1 -1
- package/dist/react/index.js +621 -274
- package/dist/react/index.js.map +1 -1
- package/dist/shared/annotation-bounds.d.ts +14 -0
- package/dist/shared/components/annotation-container.d.ts +4 -2
- package/dist/shared/components/annotations/caret.d.ts +24 -0
- package/dist/shared/components/annotations/circle.d.ts +8 -4
- package/dist/shared/components/annotations/free-text.d.ts +2 -2
- package/dist/shared/components/annotations/ink.d.ts +2 -2
- package/dist/shared/components/annotations/line.d.ts +2 -2
- package/dist/shared/components/annotations/link.d.ts +2 -2
- package/dist/shared/components/annotations/polygon.d.ts +5 -3
- package/dist/shared/components/annotations/polyline.d.ts +8 -4
- package/dist/shared/components/annotations/square.d.ts +8 -4
- package/dist/shared/components/annotations/stamp.d.ts +2 -2
- package/dist/shared/components/annotations/text.d.ts +14 -0
- package/dist/shared/components/text-markup/highlight.d.ts +2 -2
- package/dist/shared/components/text-markup/squiggly.d.ts +2 -2
- package/dist/shared/components/text-markup/strikeout.d.ts +2 -2
- package/dist/shared/components/text-markup/underline.d.ts +2 -2
- package/dist/shared/components/types.d.ts +8 -4
- package/dist/shared-preact/annotation-bounds.d.ts +14 -0
- package/dist/shared-preact/components/annotation-container.d.ts +4 -2
- package/dist/shared-preact/components/annotations/caret.d.ts +24 -0
- package/dist/shared-preact/components/annotations/circle.d.ts +8 -4
- package/dist/shared-preact/components/annotations/free-text.d.ts +2 -2
- package/dist/shared-preact/components/annotations/ink.d.ts +2 -2
- package/dist/shared-preact/components/annotations/line.d.ts +2 -2
- package/dist/shared-preact/components/annotations/link.d.ts +2 -2
- package/dist/shared-preact/components/annotations/polygon.d.ts +5 -3
- package/dist/shared-preact/components/annotations/polyline.d.ts +8 -4
- package/dist/shared-preact/components/annotations/square.d.ts +8 -4
- package/dist/shared-preact/components/annotations/stamp.d.ts +2 -2
- package/dist/shared-preact/components/annotations/text.d.ts +14 -0
- package/dist/shared-preact/components/text-markup/highlight.d.ts +2 -2
- package/dist/shared-preact/components/text-markup/squiggly.d.ts +2 -2
- package/dist/shared-preact/components/text-markup/strikeout.d.ts +2 -2
- package/dist/shared-preact/components/text-markup/underline.d.ts +2 -2
- package/dist/shared-preact/components/types.d.ts +8 -4
- package/dist/shared-react/annotation-bounds.d.ts +14 -0
- package/dist/shared-react/components/annotation-container.d.ts +4 -2
- package/dist/shared-react/components/annotations/caret.d.ts +24 -0
- package/dist/shared-react/components/annotations/circle.d.ts +8 -4
- package/dist/shared-react/components/annotations/free-text.d.ts +2 -2
- package/dist/shared-react/components/annotations/ink.d.ts +2 -2
- package/dist/shared-react/components/annotations/line.d.ts +2 -2
- package/dist/shared-react/components/annotations/link.d.ts +2 -2
- package/dist/shared-react/components/annotations/polygon.d.ts +5 -3
- package/dist/shared-react/components/annotations/polyline.d.ts +8 -4
- package/dist/shared-react/components/annotations/square.d.ts +8 -4
- package/dist/shared-react/components/annotations/stamp.d.ts +2 -2
- package/dist/shared-react/components/annotations/text.d.ts +14 -0
- package/dist/shared-react/components/text-markup/highlight.d.ts +2 -2
- package/dist/shared-react/components/text-markup/squiggly.d.ts +2 -2
- package/dist/shared-react/components/text-markup/strikeout.d.ts +2 -2
- package/dist/shared-react/components/text-markup/underline.d.ts +2 -2
- package/dist/shared-react/components/types.d.ts +8 -4
- package/dist/shared-vue/annotation-bounds.d.ts +14 -0
- package/dist/svelte/components/annotations/Caret.svelte.d.ts +13 -0
- package/dist/svelte/components/annotations/Circle.svelte.d.ts +4 -2
- package/dist/svelte/components/annotations/FreeText.svelte.d.ts +1 -1
- package/dist/svelte/components/annotations/Ink.svelte.d.ts +1 -1
- package/dist/svelte/components/annotations/Line.svelte.d.ts +1 -1
- package/dist/svelte/components/annotations/Link.svelte.d.ts +1 -1
- package/dist/svelte/components/annotations/Polygon.svelte.d.ts +2 -1
- package/dist/svelte/components/annotations/Polyline.svelte.d.ts +4 -2
- package/dist/svelte/components/annotations/Square.svelte.d.ts +4 -2
- package/dist/svelte/components/annotations/Stamp.svelte.d.ts +1 -1
- package/dist/svelte/components/annotations/Text.svelte.d.ts +10 -0
- package/dist/svelte/components/annotations/index.d.ts +1 -0
- package/dist/svelte/components/renderers/CaretRenderer.svelte.d.ts +5 -0
- package/dist/svelte/components/renderers/TextRenderer.svelte.d.ts +5 -0
- package/dist/svelte/components/text-markup/Highlight.svelte.d.ts +1 -1
- package/dist/svelte/components/text-markup/Squiggly.svelte.d.ts +1 -1
- package/dist/svelte/components/text-markup/Strikeout.svelte.d.ts +1 -1
- package/dist/svelte/components/text-markup/Underline.svelte.d.ts +1 -1
- package/dist/svelte/components/types.d.ts +2 -1
- package/dist/svelte/context/types.d.ts +7 -3
- package/dist/svelte/index.cjs +1 -1
- package/dist/svelte/index.cjs.map +1 -1
- package/dist/svelte/index.js +831 -416
- package/dist/svelte/index.js.map +1 -1
- package/dist/vue/components/annotation-container.vue.d.ts +9 -8
- package/dist/vue/components/annotation-layer.vue.d.ts +1 -1
- package/dist/vue/components/annotations/caret.vue.d.ts +24 -0
- package/dist/vue/components/annotations/circle.vue.d.ts +7 -3
- package/dist/vue/components/annotations/free-text.vue.d.ts +2 -2
- package/dist/vue/components/annotations/index.d.ts +1 -0
- package/dist/vue/components/annotations/ink.vue.d.ts +2 -2
- package/dist/vue/components/annotations/line.vue.d.ts +2 -2
- package/dist/vue/components/annotations/link.vue.d.ts +2 -2
- package/dist/vue/components/annotations/polygon.vue.d.ts +4 -2
- package/dist/vue/components/annotations/polyline.vue.d.ts +8 -3
- package/dist/vue/components/annotations/square.vue.d.ts +7 -3
- package/dist/vue/components/annotations/stamp.vue.d.ts +2 -2
- package/dist/vue/components/annotations/text.vue.d.ts +14 -0
- package/dist/vue/components/annotations.vue.d.ts +9 -10
- package/dist/vue/components/renderers/caret-renderer.vue.d.ts +6 -0
- package/dist/vue/components/renderers/text-renderer.vue.d.ts +6 -0
- package/dist/vue/components/text-markup/highlight.vue.d.ts +2 -2
- package/dist/vue/components/text-markup/squiggly.vue.d.ts +2 -2
- package/dist/vue/components/text-markup/strikeout.vue.d.ts +2 -2
- package/dist/vue/components/text-markup/underline.vue.d.ts +2 -2
- package/dist/vue/context/types.d.ts +7 -3
- package/dist/vue/index.cjs +1 -1
- package/dist/vue/index.cjs.map +1 -1
- package/dist/vue/index.js +778 -354
- package/dist/vue/index.js.map +1 -1
- package/package.json +10 -10
package/dist/index.js
CHANGED
|
@@ -156,8 +156,11 @@ function isLink(a) {
|
|
|
156
156
|
function isRedact(a) {
|
|
157
157
|
return a.object.type === PdfAnnotationSubtype.REDACT;
|
|
158
158
|
}
|
|
159
|
+
function isCaret(a) {
|
|
160
|
+
return a.object.type === PdfAnnotationSubtype.CARET;
|
|
161
|
+
}
|
|
159
162
|
function isSidebarAnnotation(a) {
|
|
160
|
-
return isTextMarkup(a) || isInk(a) || isSquare(a) || isCircle(a) || isPolygon(a) || isLine(a) || isPolyline(a) || isFreeText(a) || isStamp(a) || isRedact(a);
|
|
163
|
+
return isText(a) && !a.object.inReplyToId || isTextMarkup(a) || isInk(a) || isSquare(a) || isCircle(a) || isPolygon(a) || isLine(a) || isPolyline(a) || isFreeText(a) || isStamp(a) || isRedact(a) || isCaret(a);
|
|
161
164
|
}
|
|
162
165
|
const getAnnotationsByPageIndex = (s, page) => (s.pages[page] ?? []).map((uid) => s.byUid[uid]);
|
|
163
166
|
const getAnnotations = (s) => {
|
|
@@ -199,19 +202,33 @@ const getSidebarAnnotationsWithRepliesGroupedByPage = (s) => {
|
|
|
199
202
|
}
|
|
200
203
|
}
|
|
201
204
|
}
|
|
205
|
+
const membersByLeader = {};
|
|
206
|
+
const consumedAsGroupMember = /* @__PURE__ */ new Set();
|
|
207
|
+
for (const uidList of Object.values(s.pages)) {
|
|
208
|
+
for (const uid of uidList) {
|
|
209
|
+
const ta = s.byUid[uid];
|
|
210
|
+
if (ta && ta.object.inReplyToId && ta.object.replyType === PdfAnnotationReplyType.Group && ta.object.type !== PdfAnnotationSubtype.LINK && isSidebarAnnotation(ta)) {
|
|
211
|
+
const leaderId = ta.object.inReplyToId;
|
|
212
|
+
(membersByLeader[leaderId] || (membersByLeader[leaderId] = [])).push(ta);
|
|
213
|
+
consumedAsGroupMember.add(ta.object.id);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
202
217
|
const out = {};
|
|
203
218
|
for (const [pageStr, uidList] of Object.entries(s.pages)) {
|
|
204
219
|
const page = Number(pageStr);
|
|
205
220
|
const pageAnnotations = [];
|
|
206
221
|
for (const uid of uidList) {
|
|
207
222
|
const ta = s.byUid[uid];
|
|
208
|
-
if (ta
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
223
|
+
if (!ta || !isSidebarAnnotation(ta)) continue;
|
|
224
|
+
if (consumedAsGroupMember.has(ta.object.id)) continue;
|
|
225
|
+
const members = membersByLeader[ta.object.id];
|
|
226
|
+
pageAnnotations.push({
|
|
227
|
+
page,
|
|
228
|
+
annotation: ta,
|
|
229
|
+
replies: repliesByParent[ta.object.id] ?? [],
|
|
230
|
+
...members && members.length > 0 ? { groupMembers: members } : {}
|
|
231
|
+
});
|
|
215
232
|
}
|
|
216
233
|
if (pageAnnotations.length > 0) {
|
|
217
234
|
out[page] = pageAnnotations;
|
|
@@ -323,6 +340,62 @@ const getSelectionGroupingAction = (s) => {
|
|
|
323
340
|
}
|
|
324
341
|
return selected.length >= 2 ? "group" : "disabled";
|
|
325
342
|
};
|
|
343
|
+
const inkTools = [
|
|
344
|
+
{
|
|
345
|
+
id: "ink",
|
|
346
|
+
name: "Pen",
|
|
347
|
+
matchScore: (a) => a.type === PdfAnnotationSubtype.INK && a.intent !== "InkHighlight" ? 5 : 0,
|
|
348
|
+
interaction: {
|
|
349
|
+
exclusive: false,
|
|
350
|
+
cursor: "crosshair",
|
|
351
|
+
isDraggable: true,
|
|
352
|
+
isResizable: true,
|
|
353
|
+
lockAspectRatio: false
|
|
354
|
+
},
|
|
355
|
+
defaults: {
|
|
356
|
+
type: PdfAnnotationSubtype.INK,
|
|
357
|
+
strokeColor: "#E44234",
|
|
358
|
+
color: "#E44234",
|
|
359
|
+
// deprecated alias
|
|
360
|
+
opacity: 1,
|
|
361
|
+
strokeWidth: 6
|
|
362
|
+
},
|
|
363
|
+
behavior: {
|
|
364
|
+
commitDelay: 800
|
|
365
|
+
}
|
|
366
|
+
},
|
|
367
|
+
{
|
|
368
|
+
id: "inkHighlighter",
|
|
369
|
+
name: "Ink Highlighter",
|
|
370
|
+
matchScore: (a) => a.type === PdfAnnotationSubtype.INK && a.intent === "InkHighlight" ? 10 : 0,
|
|
371
|
+
interaction: {
|
|
372
|
+
exclusive: false,
|
|
373
|
+
cursor: "crosshair",
|
|
374
|
+
isDraggable: true,
|
|
375
|
+
isResizable: true,
|
|
376
|
+
lockAspectRatio: false,
|
|
377
|
+
lockGroupAspectRatio: (a) => {
|
|
378
|
+
const r2 = ((a.rotation ?? 0) % 90 + 90) % 90;
|
|
379
|
+
return r2 >= 6 && r2 <= 84;
|
|
380
|
+
}
|
|
381
|
+
},
|
|
382
|
+
defaults: {
|
|
383
|
+
type: PdfAnnotationSubtype.INK,
|
|
384
|
+
intent: "InkHighlight",
|
|
385
|
+
strokeColor: "#FFCD45",
|
|
386
|
+
color: "#FFCD45",
|
|
387
|
+
// deprecated alias
|
|
388
|
+
opacity: 1,
|
|
389
|
+
strokeWidth: 14,
|
|
390
|
+
blendMode: PdfBlendMode.Multiply
|
|
391
|
+
},
|
|
392
|
+
behavior: {
|
|
393
|
+
commitDelay: 800,
|
|
394
|
+
smartLineRecognition: true,
|
|
395
|
+
smartLineThreshold: 0.15
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
];
|
|
326
399
|
const defaultTools = [
|
|
327
400
|
// Text Markup Tools
|
|
328
401
|
{
|
|
@@ -334,6 +407,7 @@ const defaultTools = [
|
|
|
334
407
|
textSelection: true,
|
|
335
408
|
isDraggable: false,
|
|
336
409
|
isResizable: false,
|
|
410
|
+
isRotatable: false,
|
|
337
411
|
// Text markup annotations are anchored to text and should not move/resize in groups
|
|
338
412
|
isGroupDraggable: false,
|
|
339
413
|
isGroupResizable: false
|
|
@@ -356,6 +430,7 @@ const defaultTools = [
|
|
|
356
430
|
textSelection: true,
|
|
357
431
|
isDraggable: false,
|
|
358
432
|
isResizable: false,
|
|
433
|
+
isRotatable: false,
|
|
359
434
|
isGroupDraggable: false,
|
|
360
435
|
isGroupResizable: false
|
|
361
436
|
},
|
|
@@ -376,6 +451,7 @@ const defaultTools = [
|
|
|
376
451
|
textSelection: true,
|
|
377
452
|
isDraggable: false,
|
|
378
453
|
isResizable: false,
|
|
454
|
+
isRotatable: false,
|
|
379
455
|
isGroupDraggable: false,
|
|
380
456
|
isGroupResizable: false
|
|
381
457
|
},
|
|
@@ -396,6 +472,7 @@ const defaultTools = [
|
|
|
396
472
|
textSelection: true,
|
|
397
473
|
isDraggable: false,
|
|
398
474
|
isResizable: false,
|
|
475
|
+
isRotatable: false,
|
|
399
476
|
isGroupDraggable: false,
|
|
400
477
|
isGroupResizable: false
|
|
401
478
|
},
|
|
@@ -407,53 +484,61 @@ const defaultTools = [
|
|
|
407
484
|
opacity: 1
|
|
408
485
|
}
|
|
409
486
|
},
|
|
410
|
-
//
|
|
487
|
+
// Insert Text (Caret with intent Insert)
|
|
411
488
|
{
|
|
412
|
-
id: "
|
|
413
|
-
name: "
|
|
414
|
-
matchScore: (a) =>
|
|
489
|
+
id: "insertText",
|
|
490
|
+
name: "Insert Text",
|
|
491
|
+
matchScore: (a) => {
|
|
492
|
+
var _a;
|
|
493
|
+
if (a.type !== PdfAnnotationSubtype.CARET) return 0;
|
|
494
|
+
return ((_a = a.intent) == null ? void 0 : _a.includes("Insert")) ? 2 : 1;
|
|
495
|
+
},
|
|
415
496
|
interaction: {
|
|
416
497
|
exclusive: false,
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
498
|
+
textSelection: true,
|
|
499
|
+
showSelectionRects: true,
|
|
500
|
+
isDraggable: false,
|
|
501
|
+
isResizable: false,
|
|
502
|
+
isRotatable: false,
|
|
503
|
+
isGroupDraggable: false,
|
|
504
|
+
isGroupResizable: false
|
|
421
505
|
},
|
|
422
506
|
defaults: {
|
|
423
|
-
type: PdfAnnotationSubtype.
|
|
507
|
+
type: PdfAnnotationSubtype.CARET,
|
|
424
508
|
strokeColor: "#E44234",
|
|
425
|
-
color: "#E44234",
|
|
426
|
-
// deprecated alias
|
|
427
509
|
opacity: 1,
|
|
428
|
-
|
|
510
|
+
intent: "Insert"
|
|
429
511
|
}
|
|
430
512
|
},
|
|
513
|
+
// Replace Text (StrikeOut + Caret group)
|
|
431
514
|
{
|
|
432
|
-
id: "
|
|
433
|
-
name: "
|
|
434
|
-
matchScore: (a) =>
|
|
515
|
+
id: "replaceText",
|
|
516
|
+
name: "Replace Text",
|
|
517
|
+
matchScore: (a) => {
|
|
518
|
+
var _a, _b;
|
|
519
|
+
if (a.type === PdfAnnotationSubtype.STRIKEOUT && ((_a = a.intent) == null ? void 0 : _a.includes("StrikeOutTextEdit")))
|
|
520
|
+
return 2;
|
|
521
|
+
if (a.type === PdfAnnotationSubtype.CARET && ((_b = a.intent) == null ? void 0 : _b.includes("Replace"))) return 2;
|
|
522
|
+
return 0;
|
|
523
|
+
},
|
|
435
524
|
interaction: {
|
|
436
525
|
exclusive: false,
|
|
437
|
-
|
|
438
|
-
isDraggable:
|
|
439
|
-
isResizable:
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
return r >= 6 && r <= 84;
|
|
444
|
-
}
|
|
526
|
+
textSelection: true,
|
|
527
|
+
isDraggable: false,
|
|
528
|
+
isResizable: false,
|
|
529
|
+
isRotatable: false,
|
|
530
|
+
isGroupDraggable: false,
|
|
531
|
+
isGroupResizable: false
|
|
445
532
|
},
|
|
446
533
|
defaults: {
|
|
447
|
-
type: PdfAnnotationSubtype.
|
|
448
|
-
|
|
449
|
-
strokeColor: "#FFCD45",
|
|
450
|
-
color: "#FFCD45",
|
|
451
|
-
// deprecated alias
|
|
534
|
+
type: PdfAnnotationSubtype.STRIKEOUT,
|
|
535
|
+
strokeColor: "#E44234",
|
|
452
536
|
opacity: 1,
|
|
453
|
-
|
|
454
|
-
blendMode: PdfBlendMode.Multiply
|
|
537
|
+
intent: "StrikeOutTextEdit"
|
|
455
538
|
}
|
|
456
539
|
},
|
|
540
|
+
// Drawing Tools
|
|
541
|
+
...inkTools,
|
|
457
542
|
// Shape Tools
|
|
458
543
|
{
|
|
459
544
|
id: "circle",
|
|
@@ -466,8 +551,8 @@ const defaultTools = [
|
|
|
466
551
|
isResizable: true,
|
|
467
552
|
lockAspectRatio: false,
|
|
468
553
|
lockGroupAspectRatio: (a) => {
|
|
469
|
-
const
|
|
470
|
-
return
|
|
554
|
+
const r2 = ((a.rotation ?? 0) % 90 + 90) % 90;
|
|
555
|
+
return r2 >= 6 && r2 <= 84;
|
|
471
556
|
}
|
|
472
557
|
},
|
|
473
558
|
defaults: {
|
|
@@ -494,8 +579,8 @@ const defaultTools = [
|
|
|
494
579
|
isResizable: true,
|
|
495
580
|
lockAspectRatio: false,
|
|
496
581
|
lockGroupAspectRatio: (a) => {
|
|
497
|
-
const
|
|
498
|
-
return
|
|
582
|
+
const r2 = ((a.rotation ?? 0) % 90 + 90) % 90;
|
|
583
|
+
return r2 >= 6 && r2 <= 84;
|
|
499
584
|
}
|
|
500
585
|
},
|
|
501
586
|
defaults: {
|
|
@@ -525,8 +610,8 @@ const defaultTools = [
|
|
|
525
610
|
isGroupResizable: true,
|
|
526
611
|
// Scales proportionally in a group
|
|
527
612
|
lockGroupAspectRatio: (a) => {
|
|
528
|
-
const
|
|
529
|
-
return
|
|
613
|
+
const r2 = ((a.rotation ?? 0) % 90 + 90) % 90;
|
|
614
|
+
return r2 >= 6 && r2 <= 84;
|
|
530
615
|
}
|
|
531
616
|
},
|
|
532
617
|
defaults: {
|
|
@@ -556,8 +641,8 @@ const defaultTools = [
|
|
|
556
641
|
isGroupResizable: true,
|
|
557
642
|
// Scales proportionally in a group
|
|
558
643
|
lockGroupAspectRatio: (a) => {
|
|
559
|
-
const
|
|
560
|
-
return
|
|
644
|
+
const r2 = ((a.rotation ?? 0) % 90 + 90) % 90;
|
|
645
|
+
return r2 >= 6 && r2 <= 84;
|
|
561
646
|
}
|
|
562
647
|
},
|
|
563
648
|
defaults: {
|
|
@@ -592,8 +677,8 @@ const defaultTools = [
|
|
|
592
677
|
isGroupResizable: true,
|
|
593
678
|
// Scales proportionally in a group
|
|
594
679
|
lockGroupAspectRatio: (a) => {
|
|
595
|
-
const
|
|
596
|
-
return
|
|
680
|
+
const r2 = ((a.rotation ?? 0) % 90 + 90) % 90;
|
|
681
|
+
return r2 >= 6 && r2 <= 84;
|
|
597
682
|
}
|
|
598
683
|
},
|
|
599
684
|
defaults: {
|
|
@@ -618,8 +703,8 @@ const defaultTools = [
|
|
|
618
703
|
isGroupResizable: true,
|
|
619
704
|
// Scales proportionally in a group
|
|
620
705
|
lockGroupAspectRatio: (a) => {
|
|
621
|
-
const
|
|
622
|
-
return
|
|
706
|
+
const r2 = ((a.rotation ?? 0) % 90 + 90) % 90;
|
|
707
|
+
return r2 >= 6 && r2 <= 84;
|
|
623
708
|
}
|
|
624
709
|
},
|
|
625
710
|
defaults: {
|
|
@@ -631,6 +716,26 @@ const defaultTools = [
|
|
|
631
716
|
}
|
|
632
717
|
},
|
|
633
718
|
// Text & Stamp
|
|
719
|
+
{
|
|
720
|
+
id: "textComment",
|
|
721
|
+
name: "Comment",
|
|
722
|
+
matchScore: (a) => a.type === PdfAnnotationSubtype.TEXT && !a.inReplyToId ? 1 : 0,
|
|
723
|
+
interaction: {
|
|
724
|
+
exclusive: false,
|
|
725
|
+
cursor: "crosshair",
|
|
726
|
+
isDraggable: true,
|
|
727
|
+
isResizable: false,
|
|
728
|
+
isRotatable: false
|
|
729
|
+
},
|
|
730
|
+
defaults: {
|
|
731
|
+
type: PdfAnnotationSubtype.TEXT,
|
|
732
|
+
strokeColor: "#FFCD45",
|
|
733
|
+
opacity: 1
|
|
734
|
+
},
|
|
735
|
+
behavior: {
|
|
736
|
+
selectAfterCreate: true
|
|
737
|
+
}
|
|
738
|
+
},
|
|
634
739
|
{
|
|
635
740
|
id: "freeText",
|
|
636
741
|
name: "Free Text",
|
|
@@ -642,8 +747,8 @@ const defaultTools = [
|
|
|
642
747
|
isResizable: true,
|
|
643
748
|
lockAspectRatio: false,
|
|
644
749
|
lockGroupAspectRatio: (a) => {
|
|
645
|
-
const
|
|
646
|
-
return
|
|
750
|
+
const r2 = ((a.rotation ?? 0) % 90 + 90) % 90;
|
|
751
|
+
return r2 >= 6 && r2 <= 84;
|
|
647
752
|
}
|
|
648
753
|
},
|
|
649
754
|
defaults: {
|
|
@@ -952,7 +1057,7 @@ const reducer = (state, action) => {
|
|
|
952
1057
|
},
|
|
953
1058
|
byUid: {
|
|
954
1059
|
...docState.byUid,
|
|
955
|
-
[uid]: { commitState: "new", object: annotation }
|
|
1060
|
+
[uid]: { commitState: "new", object: annotation, dictMode: true }
|
|
956
1061
|
},
|
|
957
1062
|
hasPendingChanges: true
|
|
958
1063
|
}
|
|
@@ -1086,6 +1191,18 @@ function useState(initialValue) {
|
|
|
1086
1191
|
};
|
|
1087
1192
|
return [getValue, setValue];
|
|
1088
1193
|
}
|
|
1194
|
+
function isLineLike(points, threshold) {
|
|
1195
|
+
if (points.length < 3) return true;
|
|
1196
|
+
const A = points[0];
|
|
1197
|
+
const B = points[points.length - 1];
|
|
1198
|
+
const len = Math.hypot(B.x - A.x, B.y - A.y);
|
|
1199
|
+
if (len < 5) return false;
|
|
1200
|
+
const maxDev = points.reduce((max, P) => {
|
|
1201
|
+
const d = Math.abs((B.x - A.x) * (A.y - P.y) - (A.x - P.x) * (B.y - A.y)) / len;
|
|
1202
|
+
return Math.max(max, d);
|
|
1203
|
+
}, 0);
|
|
1204
|
+
return maxDev / len < threshold;
|
|
1205
|
+
}
|
|
1089
1206
|
const inkHandlerFactory = {
|
|
1090
1207
|
annotationType: PdfAnnotationSubtype.INK,
|
|
1091
1208
|
create(context) {
|
|
@@ -1121,7 +1238,8 @@ const inkHandlerFactory = {
|
|
|
1121
1238
|
data: {
|
|
1122
1239
|
...defaults,
|
|
1123
1240
|
rect: bounds,
|
|
1124
|
-
inkList: strokes
|
|
1241
|
+
inkList: strokes,
|
|
1242
|
+
blendMode: defaults.blendMode
|
|
1125
1243
|
}
|
|
1126
1244
|
};
|
|
1127
1245
|
};
|
|
@@ -1149,6 +1267,37 @@ const inkHandlerFactory = {
|
|
|
1149
1267
|
var _a;
|
|
1150
1268
|
setIsDrawing(false);
|
|
1151
1269
|
(_a = evt.releasePointerCapture) == null ? void 0 : _a.call(evt);
|
|
1270
|
+
const tool = getTool();
|
|
1271
|
+
const behavior = tool == null ? void 0 : tool.behavior;
|
|
1272
|
+
if (behavior == null ? void 0 : behavior.smartLineRecognition) {
|
|
1273
|
+
const threshold = behavior.smartLineThreshold ?? 0.15;
|
|
1274
|
+
const strokes = getStrokes();
|
|
1275
|
+
const last = strokes[strokes.length - 1];
|
|
1276
|
+
if (last && last.points.length > 1 && isLineLike(last.points, threshold)) {
|
|
1277
|
+
const first = last.points[0];
|
|
1278
|
+
const end = last.points[last.points.length - 1];
|
|
1279
|
+
const dx = end.x - first.x;
|
|
1280
|
+
const dy = end.y - first.y;
|
|
1281
|
+
const angleDeg = Math.atan2(Math.abs(dy), Math.abs(dx)) * (180 / Math.PI);
|
|
1282
|
+
const snapAngleDeg = behavior.snapAngleDeg ?? 15;
|
|
1283
|
+
if (angleDeg <= snapAngleDeg) {
|
|
1284
|
+
const avgY = last.points.reduce((sum, p) => sum + p.y, 0) / last.points.length;
|
|
1285
|
+
last.points = [
|
|
1286
|
+
{ x: first.x, y: avgY },
|
|
1287
|
+
{ x: end.x, y: avgY }
|
|
1288
|
+
];
|
|
1289
|
+
} else if (angleDeg >= 90 - snapAngleDeg) {
|
|
1290
|
+
const avgX = last.points.reduce((sum, p) => sum + p.x, 0) / last.points.length;
|
|
1291
|
+
last.points = [
|
|
1292
|
+
{ x: avgX, y: first.y },
|
|
1293
|
+
{ x: avgX, y: end.y }
|
|
1294
|
+
];
|
|
1295
|
+
}
|
|
1296
|
+
setStrokes([...strokes]);
|
|
1297
|
+
onPreview(getPreview());
|
|
1298
|
+
}
|
|
1299
|
+
}
|
|
1300
|
+
const commitDelay = (behavior == null ? void 0 : behavior.commitDelay) ?? 800;
|
|
1152
1301
|
if (timerRef.current) clearTimeout(timerRef.current);
|
|
1153
1302
|
timerRef.current = setTimeout(() => {
|
|
1154
1303
|
const strokes = getStrokes();
|
|
@@ -1169,7 +1318,7 @@ const inkHandlerFactory = {
|
|
|
1169
1318
|
}
|
|
1170
1319
|
setStrokes([]);
|
|
1171
1320
|
onPreview(null);
|
|
1172
|
-
},
|
|
1321
|
+
}, commitDelay);
|
|
1173
1322
|
},
|
|
1174
1323
|
onPointerCancel: (_, evt) => {
|
|
1175
1324
|
var _a;
|
|
@@ -1182,45 +1331,6 @@ const inkHandlerFactory = {
|
|
|
1182
1331
|
};
|
|
1183
1332
|
}
|
|
1184
1333
|
};
|
|
1185
|
-
function useClickDetector({
|
|
1186
|
-
threshold = 5,
|
|
1187
|
-
getTool,
|
|
1188
|
-
onClickDetected
|
|
1189
|
-
}) {
|
|
1190
|
-
const [getStartPos, setStartPos] = useState(null);
|
|
1191
|
-
const [getHasMoved, setHasMoved] = useState(false);
|
|
1192
|
-
return {
|
|
1193
|
-
onStart: (pos) => {
|
|
1194
|
-
setStartPos(pos);
|
|
1195
|
-
setHasMoved(false);
|
|
1196
|
-
},
|
|
1197
|
-
onMove: (pos) => {
|
|
1198
|
-
const start = getStartPos();
|
|
1199
|
-
if (!start || getHasMoved()) return;
|
|
1200
|
-
const distance = Math.sqrt(Math.pow(pos.x - start.x, 2) + Math.pow(pos.y - start.y, 2));
|
|
1201
|
-
if (distance > threshold) {
|
|
1202
|
-
setHasMoved(true);
|
|
1203
|
-
}
|
|
1204
|
-
},
|
|
1205
|
-
onEnd: (pos) => {
|
|
1206
|
-
var _a;
|
|
1207
|
-
const start = getStartPos();
|
|
1208
|
-
if (start && !getHasMoved()) {
|
|
1209
|
-
const tool = getTool();
|
|
1210
|
-
if (tool && "clickBehavior" in tool && ((_a = tool.clickBehavior) == null ? void 0 : _a.enabled)) {
|
|
1211
|
-
onClickDetected(pos, tool);
|
|
1212
|
-
}
|
|
1213
|
-
}
|
|
1214
|
-
setStartPos(null);
|
|
1215
|
-
setHasMoved(false);
|
|
1216
|
-
},
|
|
1217
|
-
hasMoved: getHasMoved,
|
|
1218
|
-
reset: () => {
|
|
1219
|
-
setStartPos(null);
|
|
1220
|
-
setHasMoved(false);
|
|
1221
|
-
}
|
|
1222
|
-
};
|
|
1223
|
-
}
|
|
1224
1334
|
function createArrowHandler(isClosed) {
|
|
1225
1335
|
const calculateGeometry = (sw) => {
|
|
1226
1336
|
const len = sw * 9;
|
|
@@ -1280,14 +1390,14 @@ const LINE_ENDING_HANDLERS = {
|
|
|
1280
1390
|
},
|
|
1281
1391
|
[PdfAnnotationLineEnding.Circle]: {
|
|
1282
1392
|
getSvgPath: (sw) => {
|
|
1283
|
-
const
|
|
1284
|
-
return `M ${
|
|
1393
|
+
const r2 = sw * 5 / 2;
|
|
1394
|
+
return `M ${r2} 0 A ${r2} ${r2} 0 1 1 ${-r2} 0 A ${r2} ${r2} 0 1 1 ${r2} 0`;
|
|
1285
1395
|
},
|
|
1286
1396
|
getLocalPoints: (sw) => {
|
|
1287
|
-
const
|
|
1397
|
+
const r2 = sw * 5 / 2;
|
|
1288
1398
|
return [
|
|
1289
|
-
{ x: -
|
|
1290
|
-
{ x:
|
|
1399
|
+
{ x: -r2, y: -r2 },
|
|
1400
|
+
{ x: r2, y: r2 }
|
|
1291
1401
|
];
|
|
1292
1402
|
},
|
|
1293
1403
|
getRotation: () => 0,
|
|
@@ -1429,7 +1539,7 @@ function createEnding(ending, strokeWidth, rad, px, py) {
|
|
|
1429
1539
|
if (!ending) return null;
|
|
1430
1540
|
const handler = LINE_ENDING_HANDLERS[ending];
|
|
1431
1541
|
if (!handler) return null;
|
|
1432
|
-
const toDeg = (
|
|
1542
|
+
const toDeg = (r2) => r2 * 180 / Math.PI;
|
|
1433
1543
|
const rotationAngle = handler.getRotation(rad);
|
|
1434
1544
|
return {
|
|
1435
1545
|
d: handler.getSvgPath(strokeWidth),
|
|
@@ -1601,6 +1711,75 @@ const index = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.definePropert
|
|
|
1601
1711
|
rotatePointAroundCenter: rotatePointAround,
|
|
1602
1712
|
rotateVertices
|
|
1603
1713
|
}, Symbol.toStringTag, { value: "Module" }));
|
|
1714
|
+
const COMMENT_SIZE = 24;
|
|
1715
|
+
const textHandlerFactory = {
|
|
1716
|
+
annotationType: PdfAnnotationSubtype.TEXT,
|
|
1717
|
+
create(context) {
|
|
1718
|
+
const { onCommit, getTool, pageSize } = context;
|
|
1719
|
+
return {
|
|
1720
|
+
onPointerDown: (pos) => {
|
|
1721
|
+
const tool = getTool();
|
|
1722
|
+
if (!tool) return;
|
|
1723
|
+
const rect = {
|
|
1724
|
+
origin: { x: pos.x - COMMENT_SIZE / 2, y: pos.y - COMMENT_SIZE / 2 },
|
|
1725
|
+
size: { width: COMMENT_SIZE, height: COMMENT_SIZE }
|
|
1726
|
+
};
|
|
1727
|
+
let anno = {
|
|
1728
|
+
...tool.defaults,
|
|
1729
|
+
rect,
|
|
1730
|
+
type: PdfAnnotationSubtype.TEXT,
|
|
1731
|
+
icon: tool.defaults.icon ?? PdfAnnotationIcon.Comment,
|
|
1732
|
+
contents: tool.defaults.contents ?? "",
|
|
1733
|
+
flags: tool.defaults.flags ?? ["print", "noRotate", "noZoom"],
|
|
1734
|
+
pageIndex: context.pageIndex,
|
|
1735
|
+
id: uuidV4(),
|
|
1736
|
+
created: /* @__PURE__ */ new Date()
|
|
1737
|
+
};
|
|
1738
|
+
anno = clampAnnotationToPage(anno, pageSize);
|
|
1739
|
+
onCommit(anno);
|
|
1740
|
+
}
|
|
1741
|
+
};
|
|
1742
|
+
}
|
|
1743
|
+
};
|
|
1744
|
+
function useClickDetector({
|
|
1745
|
+
threshold = 5,
|
|
1746
|
+
getTool,
|
|
1747
|
+
onClickDetected
|
|
1748
|
+
}) {
|
|
1749
|
+
const [getStartPos, setStartPos] = useState(null);
|
|
1750
|
+
const [getHasMoved, setHasMoved] = useState(false);
|
|
1751
|
+
return {
|
|
1752
|
+
onStart: (pos) => {
|
|
1753
|
+
setStartPos(pos);
|
|
1754
|
+
setHasMoved(false);
|
|
1755
|
+
},
|
|
1756
|
+
onMove: (pos) => {
|
|
1757
|
+
const start = getStartPos();
|
|
1758
|
+
if (!start || getHasMoved()) return;
|
|
1759
|
+
const distance2 = Math.sqrt(Math.pow(pos.x - start.x, 2) + Math.pow(pos.y - start.y, 2));
|
|
1760
|
+
if (distance2 > threshold) {
|
|
1761
|
+
setHasMoved(true);
|
|
1762
|
+
}
|
|
1763
|
+
},
|
|
1764
|
+
onEnd: (pos) => {
|
|
1765
|
+
var _a;
|
|
1766
|
+
const start = getStartPos();
|
|
1767
|
+
if (start && !getHasMoved()) {
|
|
1768
|
+
const tool = getTool();
|
|
1769
|
+
if (tool && "clickBehavior" in tool && ((_a = tool.clickBehavior) == null ? void 0 : _a.enabled)) {
|
|
1770
|
+
onClickDetected(pos, tool);
|
|
1771
|
+
}
|
|
1772
|
+
}
|
|
1773
|
+
setStartPos(null);
|
|
1774
|
+
setHasMoved(false);
|
|
1775
|
+
},
|
|
1776
|
+
hasMoved: getHasMoved,
|
|
1777
|
+
reset: () => {
|
|
1778
|
+
setStartPos(null);
|
|
1779
|
+
setHasMoved(false);
|
|
1780
|
+
}
|
|
1781
|
+
};
|
|
1782
|
+
}
|
|
1604
1783
|
const freeTextHandlerFactory = {
|
|
1605
1784
|
annotationType: PdfAnnotationSubtype.FREETEXT,
|
|
1606
1785
|
create(context) {
|
|
@@ -2003,6 +2182,554 @@ const polylineHandlerFactory = {
|
|
|
2003
2182
|
};
|
|
2004
2183
|
}
|
|
2005
2184
|
};
|
|
2185
|
+
function convertAABBRectToUnrotatedSpace(newAABBRect, originalAABBRect, originalUnrotatedRect, rotationDegrees) {
|
|
2186
|
+
const theta = rotationDegrees * Math.PI / 180;
|
|
2187
|
+
const A = Math.abs(Math.cos(theta));
|
|
2188
|
+
const B = Math.abs(Math.sin(theta));
|
|
2189
|
+
const det = A * A - B * B;
|
|
2190
|
+
const newAABBw = newAABBRect.size.width;
|
|
2191
|
+
const newAABBh = newAABBRect.size.height;
|
|
2192
|
+
let newWidth;
|
|
2193
|
+
let newHeight;
|
|
2194
|
+
if (Math.abs(det) > 1e-6) {
|
|
2195
|
+
newWidth = (A * newAABBw - B * newAABBh) / det;
|
|
2196
|
+
newHeight = (A * newAABBh - B * newAABBw) / det;
|
|
2197
|
+
newWidth = Math.max(newWidth, 1);
|
|
2198
|
+
newHeight = Math.max(newHeight, 1);
|
|
2199
|
+
} else {
|
|
2200
|
+
const origArea = originalAABBRect.size.width * originalAABBRect.size.height;
|
|
2201
|
+
const newArea = newAABBw * newAABBh;
|
|
2202
|
+
const uniformScale = origArea > 0 ? Math.sqrt(newArea / origArea) : 1;
|
|
2203
|
+
newWidth = originalUnrotatedRect.size.width * uniformScale;
|
|
2204
|
+
newHeight = originalUnrotatedRect.size.height * uniformScale;
|
|
2205
|
+
}
|
|
2206
|
+
const newCenterX = newAABBRect.origin.x + newAABBw / 2;
|
|
2207
|
+
const newCenterY = newAABBRect.origin.y + newAABBh / 2;
|
|
2208
|
+
return {
|
|
2209
|
+
origin: { x: newCenterX - newWidth / 2, y: newCenterY - newHeight / 2 },
|
|
2210
|
+
size: { width: newWidth, height: newHeight }
|
|
2211
|
+
};
|
|
2212
|
+
}
|
|
2213
|
+
const ANGLE_180 = Math.PI;
|
|
2214
|
+
const ANGLE_90 = Math.PI / 2;
|
|
2215
|
+
const ANGLE_34 = 34 * Math.PI / 180;
|
|
2216
|
+
const ANGLE_30 = 30 * Math.PI / 180;
|
|
2217
|
+
const ANGLE_12 = 12 * Math.PI / 180;
|
|
2218
|
+
class PathBuilder {
|
|
2219
|
+
constructor() {
|
|
2220
|
+
this.parts = [];
|
|
2221
|
+
this.bbox = {
|
|
2222
|
+
minX: Infinity,
|
|
2223
|
+
minY: Infinity,
|
|
2224
|
+
maxX: -Infinity,
|
|
2225
|
+
maxY: -Infinity
|
|
2226
|
+
};
|
|
2227
|
+
this.started = false;
|
|
2228
|
+
}
|
|
2229
|
+
moveTo(x, y) {
|
|
2230
|
+
const sy = -y;
|
|
2231
|
+
this.updateBBox(x, sy);
|
|
2232
|
+
this.parts.push(`M ${r(x)} ${r(sy)}`);
|
|
2233
|
+
this.started = true;
|
|
2234
|
+
}
|
|
2235
|
+
curveTo(x1, y1, x2, y2, x3, y3) {
|
|
2236
|
+
const sy1 = -y1;
|
|
2237
|
+
const sy2 = -y2;
|
|
2238
|
+
const sy3 = -y3;
|
|
2239
|
+
this.updateBBox(x1, sy1);
|
|
2240
|
+
this.updateBBox(x2, sy2);
|
|
2241
|
+
this.updateBBox(x3, sy3);
|
|
2242
|
+
this.parts.push(`C ${r(x1)} ${r(sy1)}, ${r(x2)} ${r(sy2)}, ${r(x3)} ${r(sy3)}`);
|
|
2243
|
+
}
|
|
2244
|
+
close() {
|
|
2245
|
+
if (this.started) {
|
|
2246
|
+
this.parts.push("Z");
|
|
2247
|
+
}
|
|
2248
|
+
}
|
|
2249
|
+
build(lineWidth) {
|
|
2250
|
+
const d = lineWidth > 0 ? lineWidth / 2 : 0;
|
|
2251
|
+
return {
|
|
2252
|
+
path: this.parts.join(" "),
|
|
2253
|
+
bbox: {
|
|
2254
|
+
minX: this.bbox.minX - d,
|
|
2255
|
+
minY: this.bbox.minY - d,
|
|
2256
|
+
maxX: this.bbox.maxX + d,
|
|
2257
|
+
maxY: this.bbox.maxY + d
|
|
2258
|
+
}
|
|
2259
|
+
};
|
|
2260
|
+
}
|
|
2261
|
+
updateBBox(x, y) {
|
|
2262
|
+
if (x < this.bbox.minX) this.bbox.minX = x;
|
|
2263
|
+
if (y < this.bbox.minY) this.bbox.minY = y;
|
|
2264
|
+
if (x > this.bbox.maxX) this.bbox.maxX = x;
|
|
2265
|
+
if (y > this.bbox.maxY) this.bbox.maxY = y;
|
|
2266
|
+
}
|
|
2267
|
+
}
|
|
2268
|
+
function r(n) {
|
|
2269
|
+
return Number(n.toFixed(4)).toString();
|
|
2270
|
+
}
|
|
2271
|
+
function distance(a, b) {
|
|
2272
|
+
const dx = b.x - a.x;
|
|
2273
|
+
const dy = b.y - a.y;
|
|
2274
|
+
return Math.sqrt(dx * dx + dy * dy);
|
|
2275
|
+
}
|
|
2276
|
+
function cosine(dx, hypot) {
|
|
2277
|
+
return hypot === 0 ? 0 : dx / hypot;
|
|
2278
|
+
}
|
|
2279
|
+
function sine(dy, hypot) {
|
|
2280
|
+
return hypot === 0 ? 0 : dy / hypot;
|
|
2281
|
+
}
|
|
2282
|
+
function polygonDirection(pts) {
|
|
2283
|
+
let a = 0;
|
|
2284
|
+
const len = pts.length;
|
|
2285
|
+
for (let i = 0; i < len; i++) {
|
|
2286
|
+
const j = (i + 1) % len;
|
|
2287
|
+
a += pts[i].x * pts[j].y - pts[i].y * pts[j].x;
|
|
2288
|
+
}
|
|
2289
|
+
return a;
|
|
2290
|
+
}
|
|
2291
|
+
function ensurePositiveWinding(pts) {
|
|
2292
|
+
if (polygonDirection(pts) < 0) {
|
|
2293
|
+
pts.reverse();
|
|
2294
|
+
}
|
|
2295
|
+
}
|
|
2296
|
+
function removeZeroLengthSegments(polygon) {
|
|
2297
|
+
if (polygon.length <= 2) return polygon;
|
|
2298
|
+
const tolerance = 0.5;
|
|
2299
|
+
const result = [polygon[0]];
|
|
2300
|
+
for (let i = 1; i < polygon.length; i++) {
|
|
2301
|
+
const prev = result[result.length - 1];
|
|
2302
|
+
const cur = polygon[i];
|
|
2303
|
+
if (Math.abs(cur.x - prev.x) >= tolerance || Math.abs(cur.y - prev.y) >= tolerance) {
|
|
2304
|
+
result.push(cur);
|
|
2305
|
+
}
|
|
2306
|
+
}
|
|
2307
|
+
return result;
|
|
2308
|
+
}
|
|
2309
|
+
function arcSegment(startAng, endAng, cx, cy, rx, ry, out, addMoveTo) {
|
|
2310
|
+
const cosA = Math.cos(startAng);
|
|
2311
|
+
const sinA = Math.sin(startAng);
|
|
2312
|
+
const cosB = Math.cos(endAng);
|
|
2313
|
+
const sinB = Math.sin(endAng);
|
|
2314
|
+
const denom = Math.sin((endAng - startAng) / 2);
|
|
2315
|
+
if (denom === 0) {
|
|
2316
|
+
if (addMoveTo) {
|
|
2317
|
+
out.moveTo(cx + rx * cosA, cy + ry * sinA);
|
|
2318
|
+
}
|
|
2319
|
+
return;
|
|
2320
|
+
}
|
|
2321
|
+
const bcp = 4 / 3 * (1 - Math.cos((endAng - startAng) / 2)) / denom;
|
|
2322
|
+
const p1x = cx + rx * (cosA - bcp * sinA);
|
|
2323
|
+
const p1y = cy + ry * (sinA + bcp * cosA);
|
|
2324
|
+
const p2x = cx + rx * (cosB + bcp * sinB);
|
|
2325
|
+
const p2y = cy + ry * (sinB - bcp * cosB);
|
|
2326
|
+
const p3x = cx + rx * cosB;
|
|
2327
|
+
const p3y = cy + ry * sinB;
|
|
2328
|
+
if (addMoveTo) {
|
|
2329
|
+
out.moveTo(cx + rx * cosA, cy + ry * sinA);
|
|
2330
|
+
}
|
|
2331
|
+
out.curveTo(p1x, p1y, p2x, p2y, p3x, p3y);
|
|
2332
|
+
}
|
|
2333
|
+
function arcSegmentToArray(startAng, endAng, cx, cy, rx, ry) {
|
|
2334
|
+
const cosA = Math.cos(startAng);
|
|
2335
|
+
const sinA = Math.sin(startAng);
|
|
2336
|
+
const cosB = Math.cos(endAng);
|
|
2337
|
+
const sinB = Math.sin(endAng);
|
|
2338
|
+
const denom = Math.sin((endAng - startAng) / 2);
|
|
2339
|
+
if (denom === 0) return [];
|
|
2340
|
+
const bcp = 4 / 3 * (1 - Math.cos((endAng - startAng) / 2)) / denom;
|
|
2341
|
+
return [
|
|
2342
|
+
{ x: cx + rx * (cosA - bcp * sinA), y: cy + ry * (sinA + bcp * cosA) },
|
|
2343
|
+
{ x: cx + rx * (cosB + bcp * sinB), y: cy + ry * (sinB - bcp * cosB) },
|
|
2344
|
+
{ x: cx + rx * cosB, y: cy + ry * sinB }
|
|
2345
|
+
];
|
|
2346
|
+
}
|
|
2347
|
+
function getArc(startAng, endAng, rx, ry, cx, cy, out, addMoveTo) {
|
|
2348
|
+
const angleIncr = ANGLE_90;
|
|
2349
|
+
let angleTodo = endAng - startAng;
|
|
2350
|
+
while (angleTodo < 0) angleTodo += 2 * Math.PI;
|
|
2351
|
+
const sweep = angleTodo;
|
|
2352
|
+
let angleDone = 0;
|
|
2353
|
+
if (addMoveTo) {
|
|
2354
|
+
out.moveTo(cx + rx * Math.cos(startAng), cy + ry * Math.sin(startAng));
|
|
2355
|
+
}
|
|
2356
|
+
while (angleTodo > angleIncr) {
|
|
2357
|
+
arcSegment(startAng + angleDone, startAng + angleDone + angleIncr, cx, cy, rx, ry, out, false);
|
|
2358
|
+
angleDone += angleIncr;
|
|
2359
|
+
angleTodo -= angleIncr;
|
|
2360
|
+
}
|
|
2361
|
+
if (angleTodo > 0) {
|
|
2362
|
+
arcSegment(startAng + angleDone, startAng + sweep, cx, cy, rx, ry, out, false);
|
|
2363
|
+
}
|
|
2364
|
+
}
|
|
2365
|
+
function addCornerCurl(anglePrev, angleCur, radius, cx, cy, alpha, alphaPrev, out, addMoveTo) {
|
|
2366
|
+
let a = anglePrev + ANGLE_180 + alphaPrev;
|
|
2367
|
+
const b = anglePrev + ANGLE_180 + alphaPrev - 22 * Math.PI / 180;
|
|
2368
|
+
arcSegment(a, b, cx, cy, radius, radius, out, addMoveTo);
|
|
2369
|
+
a = b;
|
|
2370
|
+
const bEnd = angleCur - alpha;
|
|
2371
|
+
getArc(a, bEnd, radius, radius, cx, cy, out, false);
|
|
2372
|
+
}
|
|
2373
|
+
function addFirstIntermediateCurl(angleCur, r2, alpha, cx, cy, out) {
|
|
2374
|
+
const a = angleCur + ANGLE_180;
|
|
2375
|
+
arcSegment(a + alpha, a + alpha - ANGLE_30, cx, cy, r2, r2, out, false);
|
|
2376
|
+
arcSegment(a + alpha - ANGLE_30, a + ANGLE_90, cx, cy, r2, r2, out, false);
|
|
2377
|
+
arcSegment(a + ANGLE_90, a + ANGLE_180 - ANGLE_34, cx, cy, r2, r2, out, false);
|
|
2378
|
+
}
|
|
2379
|
+
function getIntermediateCurlTemplate(angleCur, r2) {
|
|
2380
|
+
const pts = [];
|
|
2381
|
+
const a = angleCur + ANGLE_180;
|
|
2382
|
+
pts.push(...arcSegmentToArray(a + ANGLE_34, a + ANGLE_12, 0, 0, r2, r2));
|
|
2383
|
+
pts.push(...arcSegmentToArray(a + ANGLE_12, a + ANGLE_90, 0, 0, r2, r2));
|
|
2384
|
+
pts.push(...arcSegmentToArray(a + ANGLE_90, a + ANGLE_180 - ANGLE_34, 0, 0, r2, r2));
|
|
2385
|
+
return pts;
|
|
2386
|
+
}
|
|
2387
|
+
function outputCurlTemplate(template, x, y, out) {
|
|
2388
|
+
for (let i = 0; i + 2 < template.length; i += 3) {
|
|
2389
|
+
const a = template[i];
|
|
2390
|
+
const b = template[i + 1];
|
|
2391
|
+
const c = template[i + 2];
|
|
2392
|
+
out.curveTo(a.x + x, a.y + y, b.x + x, b.y + y, c.x + x, c.y + y);
|
|
2393
|
+
}
|
|
2394
|
+
}
|
|
2395
|
+
function computeParamsPolygon(idealRadius, k, length) {
|
|
2396
|
+
if (length === 0) return { n: -1, adjustedRadius: idealRadius };
|
|
2397
|
+
const cornerSpace = 2 * k * idealRadius;
|
|
2398
|
+
const remaining = length - cornerSpace;
|
|
2399
|
+
if (remaining <= 0) {
|
|
2400
|
+
return { n: 0, adjustedRadius: idealRadius };
|
|
2401
|
+
}
|
|
2402
|
+
const idealAdvance = 2 * k * idealRadius;
|
|
2403
|
+
const n = Math.max(1, Math.ceil(remaining / idealAdvance));
|
|
2404
|
+
const adjustedRadius = remaining / (n * 2 * k);
|
|
2405
|
+
return { n, adjustedRadius };
|
|
2406
|
+
}
|
|
2407
|
+
function cloudyPolygonImpl(vertices, isEllipse, intensity, lineWidth, out) {
|
|
2408
|
+
let polygon = removeZeroLengthSegments(vertices);
|
|
2409
|
+
ensurePositiveWinding(polygon);
|
|
2410
|
+
const numPoints = polygon.length;
|
|
2411
|
+
if (numPoints < 2) return;
|
|
2412
|
+
if (intensity <= 0) {
|
|
2413
|
+
out.moveTo(polygon[0].x, polygon[0].y);
|
|
2414
|
+
for (let i = 1; i < numPoints; i++) {
|
|
2415
|
+
out.curveTo(
|
|
2416
|
+
polygon[i].x,
|
|
2417
|
+
polygon[i].y,
|
|
2418
|
+
polygon[i].x,
|
|
2419
|
+
polygon[i].y,
|
|
2420
|
+
polygon[i].x,
|
|
2421
|
+
polygon[i].y
|
|
2422
|
+
);
|
|
2423
|
+
}
|
|
2424
|
+
return;
|
|
2425
|
+
}
|
|
2426
|
+
let idealRadius = isEllipse ? getEllipseCloudRadius(intensity, lineWidth) : getPolygonCloudRadius(intensity, lineWidth);
|
|
2427
|
+
if (idealRadius < 0.5) idealRadius = 0.5;
|
|
2428
|
+
const k = Math.cos(ANGLE_34);
|
|
2429
|
+
const edgeAlphas = [];
|
|
2430
|
+
for (let j = 0; j + 1 < numPoints; j++) {
|
|
2431
|
+
const len = distance(polygon[j], polygon[j + 1]);
|
|
2432
|
+
if (len <= 0 || len >= 2 * k * idealRadius) {
|
|
2433
|
+
edgeAlphas.push(ANGLE_34);
|
|
2434
|
+
} else {
|
|
2435
|
+
edgeAlphas.push(Math.acos(Math.min(1, len / (2 * idealRadius))));
|
|
2436
|
+
}
|
|
2437
|
+
}
|
|
2438
|
+
let anglePrev = 0;
|
|
2439
|
+
let outputStarted = false;
|
|
2440
|
+
for (let j = 0; j + 1 < numPoints; j++) {
|
|
2441
|
+
const pt = polygon[j];
|
|
2442
|
+
const ptNext = polygon[j + 1];
|
|
2443
|
+
const len = distance(pt, ptNext);
|
|
2444
|
+
if (len === 0) continue;
|
|
2445
|
+
const params = computeParamsPolygon(idealRadius, k, len);
|
|
2446
|
+
if (params.n < 0) {
|
|
2447
|
+
if (!outputStarted) {
|
|
2448
|
+
out.moveTo(pt.x, pt.y);
|
|
2449
|
+
outputStarted = true;
|
|
2450
|
+
}
|
|
2451
|
+
continue;
|
|
2452
|
+
}
|
|
2453
|
+
const edgeRadius = Math.max(0.5, params.adjustedRadius);
|
|
2454
|
+
const intermAdvance = 2 * k * edgeRadius;
|
|
2455
|
+
const firstAdvance = k * idealRadius + k * edgeRadius;
|
|
2456
|
+
let angleCur = Math.atan2(ptNext.y - pt.y, ptNext.x - pt.x);
|
|
2457
|
+
if (j === 0) {
|
|
2458
|
+
const ptPrev = polygon[numPoints - 2];
|
|
2459
|
+
anglePrev = Math.atan2(pt.y - ptPrev.y, pt.x - ptPrev.x);
|
|
2460
|
+
}
|
|
2461
|
+
const cos = cosine(ptNext.x - pt.x, len);
|
|
2462
|
+
const sin = sine(ptNext.y - pt.y, len);
|
|
2463
|
+
let x = pt.x;
|
|
2464
|
+
let y = pt.y;
|
|
2465
|
+
const alpha = edgeAlphas[j];
|
|
2466
|
+
const prevEdgeIdx = j === 0 ? numPoints - 2 : j - 1;
|
|
2467
|
+
const alphaPrevEdge = edgeAlphas[prevEdgeIdx] ?? ANGLE_34;
|
|
2468
|
+
addCornerCurl(
|
|
2469
|
+
anglePrev,
|
|
2470
|
+
angleCur,
|
|
2471
|
+
idealRadius,
|
|
2472
|
+
pt.x,
|
|
2473
|
+
pt.y,
|
|
2474
|
+
alpha,
|
|
2475
|
+
alphaPrevEdge,
|
|
2476
|
+
out,
|
|
2477
|
+
!outputStarted
|
|
2478
|
+
);
|
|
2479
|
+
outputStarted = true;
|
|
2480
|
+
if (params.n === 0) {
|
|
2481
|
+
x += len * cos;
|
|
2482
|
+
y += len * sin;
|
|
2483
|
+
} else {
|
|
2484
|
+
x += firstAdvance * cos;
|
|
2485
|
+
y += firstAdvance * sin;
|
|
2486
|
+
let numInterm = params.n;
|
|
2487
|
+
if (params.n >= 1) {
|
|
2488
|
+
addFirstIntermediateCurl(angleCur, edgeRadius, ANGLE_34, x, y, out);
|
|
2489
|
+
x += intermAdvance * cos;
|
|
2490
|
+
y += intermAdvance * sin;
|
|
2491
|
+
numInterm = params.n - 1;
|
|
2492
|
+
}
|
|
2493
|
+
const template = getIntermediateCurlTemplate(angleCur, edgeRadius);
|
|
2494
|
+
for (let i = 0; i < numInterm; i++) {
|
|
2495
|
+
outputCurlTemplate(template, x, y, out);
|
|
2496
|
+
x += intermAdvance * cos;
|
|
2497
|
+
y += intermAdvance * sin;
|
|
2498
|
+
}
|
|
2499
|
+
}
|
|
2500
|
+
anglePrev = angleCur;
|
|
2501
|
+
}
|
|
2502
|
+
}
|
|
2503
|
+
function flattenEllipse(left, bottom, right, top) {
|
|
2504
|
+
const cx = (left + right) / 2;
|
|
2505
|
+
const cy = (bottom + top) / 2;
|
|
2506
|
+
const rx = (right - left) / 2;
|
|
2507
|
+
const ry = (top - bottom) / 2;
|
|
2508
|
+
if (rx <= 0 || ry <= 0) return [];
|
|
2509
|
+
const numSegments = Math.max(32, Math.ceil(Math.max(rx, ry) * 2));
|
|
2510
|
+
const points = [];
|
|
2511
|
+
for (let i = 0; i <= numSegments; i++) {
|
|
2512
|
+
const angle = 2 * Math.PI * i / numSegments;
|
|
2513
|
+
points.push({
|
|
2514
|
+
x: cx + rx * Math.cos(angle),
|
|
2515
|
+
y: cy + ry * Math.sin(angle)
|
|
2516
|
+
});
|
|
2517
|
+
}
|
|
2518
|
+
return points;
|
|
2519
|
+
}
|
|
2520
|
+
function getEllipseCloudRadius(intensity, lineWidth) {
|
|
2521
|
+
return 4.75 * intensity + 0.5 * lineWidth;
|
|
2522
|
+
}
|
|
2523
|
+
function getPolygonCloudRadius(intensity, lineWidth) {
|
|
2524
|
+
return 4 * intensity + 0.5 * lineWidth;
|
|
2525
|
+
}
|
|
2526
|
+
function cloudyEllipseImpl(left, bottom, right, top, intensity, lineWidth, out) {
|
|
2527
|
+
if (intensity <= 0) {
|
|
2528
|
+
const rx = Math.abs(right - left) / 2;
|
|
2529
|
+
const ry = Math.abs(top - bottom) / 2;
|
|
2530
|
+
const cx = (left + right) / 2;
|
|
2531
|
+
const cy = (bottom + top) / 2;
|
|
2532
|
+
getArc(0, 2 * Math.PI, rx, ry, cx, cy, out, true);
|
|
2533
|
+
return;
|
|
2534
|
+
}
|
|
2535
|
+
const width = right - left;
|
|
2536
|
+
const height = top - bottom;
|
|
2537
|
+
let cloudRadius = getEllipseCloudRadius(intensity, lineWidth);
|
|
2538
|
+
const threshold1 = 0.5 * cloudRadius;
|
|
2539
|
+
if (width < threshold1 && height < threshold1) {
|
|
2540
|
+
const rx = Math.abs(right - left) / 2;
|
|
2541
|
+
const ry = Math.abs(top - bottom) / 2;
|
|
2542
|
+
const cx = (left + right) / 2;
|
|
2543
|
+
const cy = (bottom + top) / 2;
|
|
2544
|
+
getArc(0, 2 * Math.PI, rx, ry, cx, cy, out, true);
|
|
2545
|
+
return;
|
|
2546
|
+
}
|
|
2547
|
+
const threshold2 = 5;
|
|
2548
|
+
if (width < threshold2 && height > 20 || width > 20 && height < threshold2) {
|
|
2549
|
+
cloudyPolygonImpl(
|
|
2550
|
+
[
|
|
2551
|
+
{ x: left, y: bottom },
|
|
2552
|
+
{ x: right, y: bottom },
|
|
2553
|
+
{ x: right, y: top },
|
|
2554
|
+
{ x: left, y: top },
|
|
2555
|
+
{ x: left, y: bottom }
|
|
2556
|
+
],
|
|
2557
|
+
true,
|
|
2558
|
+
intensity,
|
|
2559
|
+
lineWidth,
|
|
2560
|
+
out
|
|
2561
|
+
);
|
|
2562
|
+
return;
|
|
2563
|
+
}
|
|
2564
|
+
const radiusAdj = Math.sin(ANGLE_12) * cloudRadius - 1.5;
|
|
2565
|
+
let adjLeft = left;
|
|
2566
|
+
let adjRight = right;
|
|
2567
|
+
let adjBottom = bottom;
|
|
2568
|
+
let adjTop = top;
|
|
2569
|
+
if (width > 2 * radiusAdj) {
|
|
2570
|
+
adjLeft += radiusAdj;
|
|
2571
|
+
adjRight -= radiusAdj;
|
|
2572
|
+
} else {
|
|
2573
|
+
const mid = (left + right) / 2;
|
|
2574
|
+
adjLeft = mid - 0.1;
|
|
2575
|
+
adjRight = mid + 0.1;
|
|
2576
|
+
}
|
|
2577
|
+
if (height > 2 * radiusAdj) {
|
|
2578
|
+
adjBottom += radiusAdj;
|
|
2579
|
+
adjTop -= radiusAdj;
|
|
2580
|
+
} else {
|
|
2581
|
+
const mid = (top + bottom) / 2;
|
|
2582
|
+
adjTop = mid + 0.1;
|
|
2583
|
+
adjBottom = mid - 0.1;
|
|
2584
|
+
}
|
|
2585
|
+
const flatPolygon = flattenEllipse(adjLeft, adjBottom, adjRight, adjTop);
|
|
2586
|
+
const numFlatPts = flatPolygon.length;
|
|
2587
|
+
if (numFlatPts < 2) return;
|
|
2588
|
+
let totLen = 0;
|
|
2589
|
+
for (let i = 1; i < numFlatPts; i++) {
|
|
2590
|
+
totLen += distance(flatPolygon[i - 1], flatPolygon[i]);
|
|
2591
|
+
}
|
|
2592
|
+
const k = Math.cos(ANGLE_34);
|
|
2593
|
+
let curlAdvance = 2 * k * cloudRadius;
|
|
2594
|
+
let n = Math.ceil(totLen / curlAdvance);
|
|
2595
|
+
if (n < 2) {
|
|
2596
|
+
const rx = Math.abs(right - left) / 2;
|
|
2597
|
+
const ry = Math.abs(top - bottom) / 2;
|
|
2598
|
+
const cx = (left + right) / 2;
|
|
2599
|
+
const cy = (bottom + top) / 2;
|
|
2600
|
+
getArc(0, 2 * Math.PI, rx, ry, cx, cy, out, true);
|
|
2601
|
+
return;
|
|
2602
|
+
}
|
|
2603
|
+
curlAdvance = totLen / n;
|
|
2604
|
+
cloudRadius = curlAdvance / (2 * k);
|
|
2605
|
+
if (cloudRadius < 0.5) {
|
|
2606
|
+
cloudRadius = 0.5;
|
|
2607
|
+
curlAdvance = 2 * k * cloudRadius;
|
|
2608
|
+
} else if (cloudRadius < 3) {
|
|
2609
|
+
const rx = Math.abs(right - left) / 2;
|
|
2610
|
+
const ry = Math.abs(top - bottom) / 2;
|
|
2611
|
+
const cx = (left + right) / 2;
|
|
2612
|
+
const cy = (bottom + top) / 2;
|
|
2613
|
+
getArc(0, 2 * Math.PI, rx, ry, cx, cy, out, true);
|
|
2614
|
+
return;
|
|
2615
|
+
}
|
|
2616
|
+
const centerPoints = [];
|
|
2617
|
+
let lengthRemain = 0;
|
|
2618
|
+
const comparisonToler = lineWidth * 0.1;
|
|
2619
|
+
for (let i = 0; i + 1 < numFlatPts; i++) {
|
|
2620
|
+
const p1 = flatPolygon[i];
|
|
2621
|
+
const p2 = flatPolygon[i + 1];
|
|
2622
|
+
const segDx = p2.x - p1.x;
|
|
2623
|
+
const segDy = p2.y - p1.y;
|
|
2624
|
+
const segLen = distance(p1, p2);
|
|
2625
|
+
if (segLen === 0) continue;
|
|
2626
|
+
let lengthTodo = segLen + lengthRemain;
|
|
2627
|
+
if (lengthTodo >= curlAdvance - comparisonToler || i === numFlatPts - 2) {
|
|
2628
|
+
const cos = cosine(segDx, segLen);
|
|
2629
|
+
const sin = sine(segDy, segLen);
|
|
2630
|
+
let d = curlAdvance - lengthRemain;
|
|
2631
|
+
while (lengthTodo >= curlAdvance - comparisonToler) {
|
|
2632
|
+
centerPoints.push({ x: p1.x + d * cos, y: p1.y + d * sin });
|
|
2633
|
+
lengthTodo -= curlAdvance;
|
|
2634
|
+
d += curlAdvance;
|
|
2635
|
+
}
|
|
2636
|
+
lengthRemain = Math.max(0, lengthTodo);
|
|
2637
|
+
} else {
|
|
2638
|
+
lengthRemain += segLen;
|
|
2639
|
+
}
|
|
2640
|
+
}
|
|
2641
|
+
const cpLen = centerPoints.length;
|
|
2642
|
+
let epAnglePrev = 0;
|
|
2643
|
+
let epAlphaPrev = 0;
|
|
2644
|
+
for (let i = 0; i < cpLen; i++) {
|
|
2645
|
+
const idxNext = (i + 1) % cpLen;
|
|
2646
|
+
const pt = centerPoints[i];
|
|
2647
|
+
const ptNext = centerPoints[idxNext];
|
|
2648
|
+
if (i === 0) {
|
|
2649
|
+
const ptPrev = centerPoints[cpLen - 1];
|
|
2650
|
+
epAnglePrev = Math.atan2(pt.y - ptPrev.y, pt.x - ptPrev.x);
|
|
2651
|
+
epAlphaPrev = computeParamsEllipse(ptPrev, pt, cloudRadius, curlAdvance);
|
|
2652
|
+
}
|
|
2653
|
+
const angleCur = Math.atan2(ptNext.y - pt.y, ptNext.x - pt.x);
|
|
2654
|
+
const alpha = computeParamsEllipse(pt, ptNext, cloudRadius, curlAdvance);
|
|
2655
|
+
addCornerCurl(epAnglePrev, angleCur, cloudRadius, pt.x, pt.y, alpha, epAlphaPrev, out, i === 0);
|
|
2656
|
+
epAnglePrev = angleCur;
|
|
2657
|
+
epAlphaPrev = alpha;
|
|
2658
|
+
}
|
|
2659
|
+
}
|
|
2660
|
+
function computeParamsEllipse(pt, ptNext, r2, curlAdv) {
|
|
2661
|
+
const len = distance(pt, ptNext);
|
|
2662
|
+
if (len === 0) return ANGLE_34;
|
|
2663
|
+
const e = len - curlAdv;
|
|
2664
|
+
const arg = (curlAdv / 2 + e / 2) / r2;
|
|
2665
|
+
return arg < -1 || arg > 1 ? 0 : Math.acos(arg);
|
|
2666
|
+
}
|
|
2667
|
+
function getCloudyBorderExtent(intensity, lineWidth, isEllipse) {
|
|
2668
|
+
const cloudRadius = isEllipse ? getEllipseCloudRadius(intensity, lineWidth) : getPolygonCloudRadius(intensity, lineWidth);
|
|
2669
|
+
return cloudRadius + lineWidth / 2;
|
|
2670
|
+
}
|
|
2671
|
+
function generateCloudyRectanglePath(rect, rd, intensity, lineWidth) {
|
|
2672
|
+
const out = new PathBuilder();
|
|
2673
|
+
let left = 0;
|
|
2674
|
+
let top = 0;
|
|
2675
|
+
let right = rect.width;
|
|
2676
|
+
let bottom = rect.height;
|
|
2677
|
+
if (rd) {
|
|
2678
|
+
left += rd.left;
|
|
2679
|
+
top += rd.top;
|
|
2680
|
+
right -= rd.right;
|
|
2681
|
+
bottom -= rd.bottom;
|
|
2682
|
+
} else {
|
|
2683
|
+
left += lineWidth / 2;
|
|
2684
|
+
top += lineWidth / 2;
|
|
2685
|
+
right -= lineWidth / 2;
|
|
2686
|
+
bottom -= lineWidth / 2;
|
|
2687
|
+
}
|
|
2688
|
+
const polygon = [
|
|
2689
|
+
{ x: left, y: -top },
|
|
2690
|
+
{ x: right, y: -top },
|
|
2691
|
+
{ x: right, y: -bottom },
|
|
2692
|
+
{ x: left, y: -bottom },
|
|
2693
|
+
{ x: left, y: -top }
|
|
2694
|
+
];
|
|
2695
|
+
cloudyPolygonImpl(polygon, false, intensity, lineWidth, out);
|
|
2696
|
+
out.close();
|
|
2697
|
+
return out.build(lineWidth);
|
|
2698
|
+
}
|
|
2699
|
+
function generateCloudyEllipsePath(rect, rd, intensity, lineWidth) {
|
|
2700
|
+
const out = new PathBuilder();
|
|
2701
|
+
let left = 0;
|
|
2702
|
+
let top = 0;
|
|
2703
|
+
let right = rect.width;
|
|
2704
|
+
let bottom = rect.height;
|
|
2705
|
+
if (rd) {
|
|
2706
|
+
left += rd.left;
|
|
2707
|
+
top += rd.top;
|
|
2708
|
+
right -= rd.right;
|
|
2709
|
+
bottom -= rd.bottom;
|
|
2710
|
+
}
|
|
2711
|
+
cloudyEllipseImpl(left, -bottom, right, -top, intensity, lineWidth, out);
|
|
2712
|
+
out.close();
|
|
2713
|
+
return out.build(lineWidth);
|
|
2714
|
+
}
|
|
2715
|
+
function generateCloudyPolygonPath(vertices, rectOrigin, intensity, lineWidth) {
|
|
2716
|
+
const out = new PathBuilder();
|
|
2717
|
+
if (vertices.length < 3) {
|
|
2718
|
+
return out.build(lineWidth);
|
|
2719
|
+
}
|
|
2720
|
+
const localPts = vertices.map((v) => ({
|
|
2721
|
+
x: v.x - rectOrigin.x,
|
|
2722
|
+
y: -(v.y - rectOrigin.y)
|
|
2723
|
+
}));
|
|
2724
|
+
const first = localPts[0];
|
|
2725
|
+
const last = localPts[localPts.length - 1];
|
|
2726
|
+
if (first.x !== last.x || first.y !== last.y) {
|
|
2727
|
+
localPts.push({ x: first.x, y: first.y });
|
|
2728
|
+
}
|
|
2729
|
+
cloudyPolygonImpl(localPts, false, intensity, lineWidth, out);
|
|
2730
|
+
out.close();
|
|
2731
|
+
return out.build(lineWidth);
|
|
2732
|
+
}
|
|
2006
2733
|
const HANDLE_SIZE_PX = 14;
|
|
2007
2734
|
const polygonHandlerFactory = {
|
|
2008
2735
|
annotationType: PdfAnnotationSubtype.POLYGON,
|
|
@@ -2041,7 +2768,9 @@ const polygonHandlerFactory = {
|
|
|
2041
2768
|
if (vertices.length < 3) return;
|
|
2042
2769
|
const defaults = getDefaults();
|
|
2043
2770
|
if (!defaults) return;
|
|
2044
|
-
const
|
|
2771
|
+
const intensity = defaults.cloudyBorderIntensity ?? 0;
|
|
2772
|
+
const pad = intensity > 0 ? getCloudyBorderExtent(intensity, defaults.strokeWidth, false) : defaults.strokeWidth / 2;
|
|
2773
|
+
const rect = expandRect(rectFromPoints(vertices), pad);
|
|
2045
2774
|
const anno = {
|
|
2046
2775
|
...defaults,
|
|
2047
2776
|
vertices,
|
|
@@ -2049,7 +2778,10 @@ const polygonHandlerFactory = {
|
|
|
2049
2778
|
type: PdfAnnotationSubtype.POLYGON,
|
|
2050
2779
|
pageIndex: context.pageIndex,
|
|
2051
2780
|
id: uuidV4(),
|
|
2052
|
-
created: /* @__PURE__ */ new Date()
|
|
2781
|
+
created: /* @__PURE__ */ new Date(),
|
|
2782
|
+
...intensity > 0 && {
|
|
2783
|
+
rectangleDifferences: { left: pad, top: pad, right: pad, bottom: pad }
|
|
2784
|
+
}
|
|
2053
2785
|
};
|
|
2054
2786
|
onCommit(anno);
|
|
2055
2787
|
setVertices([]);
|
|
@@ -2062,8 +2794,10 @@ const polygonHandlerFactory = {
|
|
|
2062
2794
|
if (vertices.length === 0 || !currentPos) return null;
|
|
2063
2795
|
const defaults = getDefaults();
|
|
2064
2796
|
if (!defaults) return null;
|
|
2797
|
+
const intensity = defaults.cloudyBorderIntensity ?? 0;
|
|
2798
|
+
const pad = intensity > 0 ? getCloudyBorderExtent(intensity, defaults.strokeWidth, false) : defaults.strokeWidth / 2;
|
|
2065
2799
|
const allPoints = [...vertices, currentPos];
|
|
2066
|
-
const bounds = expandRect(rectFromPoints(allPoints),
|
|
2800
|
+
const bounds = expandRect(rectFromPoints(allPoints), pad);
|
|
2067
2801
|
return {
|
|
2068
2802
|
type: PdfAnnotationSubtype.POLYGON,
|
|
2069
2803
|
bounds,
|
|
@@ -2149,10 +2883,11 @@ const squareHandlerFactory = {
|
|
|
2149
2883
|
const x = clamp(pos.x - halfWidth, 0, pageSize.width - width);
|
|
2150
2884
|
const y = clamp(pos.y - halfHeight, 0, pageSize.height - height);
|
|
2151
2885
|
const strokeWidth = defaults.strokeWidth;
|
|
2152
|
-
const
|
|
2886
|
+
const intensity = defaults.cloudyBorderIntensity ?? 0;
|
|
2887
|
+
const pad = intensity > 0 ? getCloudyBorderExtent(intensity, strokeWidth, false) : strokeWidth / 2;
|
|
2153
2888
|
const rect = {
|
|
2154
|
-
origin: { x: x -
|
|
2155
|
-
size: { width: width +
|
|
2889
|
+
origin: { x: x - pad, y: y - pad },
|
|
2890
|
+
size: { width: width + 2 * pad, height: height + 2 * pad }
|
|
2156
2891
|
};
|
|
2157
2892
|
const anno = {
|
|
2158
2893
|
...defaults,
|
|
@@ -2160,7 +2895,10 @@ const squareHandlerFactory = {
|
|
|
2160
2895
|
created: /* @__PURE__ */ new Date(),
|
|
2161
2896
|
id: uuidV4(),
|
|
2162
2897
|
pageIndex,
|
|
2163
|
-
rect
|
|
2898
|
+
rect,
|
|
2899
|
+
...intensity > 0 && {
|
|
2900
|
+
rectangleDifferences: { left: pad, top: pad, right: pad, bottom: pad }
|
|
2901
|
+
}
|
|
2164
2902
|
};
|
|
2165
2903
|
onCommit(anno);
|
|
2166
2904
|
}
|
|
@@ -2175,17 +2913,21 @@ const squareHandlerFactory = {
|
|
|
2175
2913
|
const defaults = getDefaults();
|
|
2176
2914
|
if (!defaults) return null;
|
|
2177
2915
|
const strokeWidth = defaults.strokeWidth;
|
|
2178
|
-
const
|
|
2916
|
+
const intensity = defaults.cloudyBorderIntensity ?? 0;
|
|
2917
|
+
const pad = intensity > 0 ? getCloudyBorderExtent(intensity, strokeWidth, false) : strokeWidth / 2;
|
|
2179
2918
|
const rect = {
|
|
2180
|
-
origin: { x: minX -
|
|
2181
|
-
size: { width: width +
|
|
2919
|
+
origin: { x: minX - pad, y: minY - pad },
|
|
2920
|
+
size: { width: width + 2 * pad, height: height + 2 * pad }
|
|
2182
2921
|
};
|
|
2183
2922
|
return {
|
|
2184
2923
|
type: PdfAnnotationSubtype.SQUARE,
|
|
2185
2924
|
bounds: rect,
|
|
2186
2925
|
data: {
|
|
2187
2926
|
rect,
|
|
2188
|
-
...defaults
|
|
2927
|
+
...defaults,
|
|
2928
|
+
...intensity > 0 && {
|
|
2929
|
+
rectangleDifferences: { left: pad, top: pad, right: pad, bottom: pad }
|
|
2930
|
+
}
|
|
2189
2931
|
}
|
|
2190
2932
|
};
|
|
2191
2933
|
};
|
|
@@ -2219,13 +2961,18 @@ const squareHandlerFactory = {
|
|
|
2219
2961
|
if (!defaults2) return;
|
|
2220
2962
|
const preview = getPreview(clampedPos);
|
|
2221
2963
|
if (preview) {
|
|
2964
|
+
const intensity = defaults2.cloudyBorderIntensity ?? 0;
|
|
2965
|
+
const pad = intensity > 0 ? getCloudyBorderExtent(intensity, defaults2.strokeWidth, false) : void 0;
|
|
2222
2966
|
const anno = {
|
|
2223
2967
|
...defaults2,
|
|
2224
2968
|
type: PdfAnnotationSubtype.SQUARE,
|
|
2225
2969
|
created: /* @__PURE__ */ new Date(),
|
|
2226
2970
|
id: uuidV4(),
|
|
2227
2971
|
pageIndex,
|
|
2228
|
-
rect: preview.data.rect
|
|
2972
|
+
rect: preview.data.rect,
|
|
2973
|
+
...pad !== void 0 && {
|
|
2974
|
+
rectangleDifferences: { left: pad, top: pad, right: pad, bottom: pad }
|
|
2975
|
+
}
|
|
2229
2976
|
};
|
|
2230
2977
|
onCommit(anno);
|
|
2231
2978
|
}
|
|
@@ -2349,10 +3096,11 @@ const circleHandlerFactory = {
|
|
|
2349
3096
|
const x = clamp(pos.x - halfWidth, 0, pageSize.width - width);
|
|
2350
3097
|
const y = clamp(pos.y - halfHeight, 0, pageSize.height - height);
|
|
2351
3098
|
const strokeWidth = defaults.strokeWidth;
|
|
2352
|
-
const
|
|
3099
|
+
const intensity = defaults.cloudyBorderIntensity ?? 0;
|
|
3100
|
+
const pad = intensity > 0 ? getCloudyBorderExtent(intensity, strokeWidth, true) : strokeWidth / 2;
|
|
2353
3101
|
const rect = {
|
|
2354
|
-
origin: { x: x -
|
|
2355
|
-
size: { width: width +
|
|
3102
|
+
origin: { x: x - pad, y: y - pad },
|
|
3103
|
+
size: { width: width + 2 * pad, height: height + 2 * pad }
|
|
2356
3104
|
};
|
|
2357
3105
|
const anno = {
|
|
2358
3106
|
...defaults,
|
|
@@ -2360,7 +3108,10 @@ const circleHandlerFactory = {
|
|
|
2360
3108
|
created: /* @__PURE__ */ new Date(),
|
|
2361
3109
|
id: uuidV4(),
|
|
2362
3110
|
pageIndex,
|
|
2363
|
-
rect
|
|
3111
|
+
rect,
|
|
3112
|
+
...intensity > 0 && {
|
|
3113
|
+
rectangleDifferences: { left: pad, top: pad, right: pad, bottom: pad }
|
|
3114
|
+
}
|
|
2364
3115
|
};
|
|
2365
3116
|
onCommit(anno);
|
|
2366
3117
|
}
|
|
@@ -2375,17 +3126,21 @@ const circleHandlerFactory = {
|
|
|
2375
3126
|
const defaults = getDefaults();
|
|
2376
3127
|
if (!defaults) return null;
|
|
2377
3128
|
const strokeWidth = defaults.strokeWidth;
|
|
2378
|
-
const
|
|
3129
|
+
const intensity = defaults.cloudyBorderIntensity ?? 0;
|
|
3130
|
+
const pad = intensity > 0 ? getCloudyBorderExtent(intensity, strokeWidth, true) : strokeWidth / 2;
|
|
2379
3131
|
const rect = {
|
|
2380
|
-
origin: { x: minX -
|
|
2381
|
-
size: { width: width +
|
|
3132
|
+
origin: { x: minX - pad, y: minY - pad },
|
|
3133
|
+
size: { width: width + 2 * pad, height: height + 2 * pad }
|
|
2382
3134
|
};
|
|
2383
3135
|
return {
|
|
2384
3136
|
type: PdfAnnotationSubtype.CIRCLE,
|
|
2385
3137
|
bounds: rect,
|
|
2386
3138
|
data: {
|
|
2387
3139
|
rect,
|
|
2388
|
-
...defaults
|
|
3140
|
+
...defaults,
|
|
3141
|
+
...intensity > 0 && {
|
|
3142
|
+
rectangleDifferences: { left: pad, top: pad, right: pad, bottom: pad }
|
|
3143
|
+
}
|
|
2389
3144
|
}
|
|
2390
3145
|
};
|
|
2391
3146
|
};
|
|
@@ -2419,6 +3174,8 @@ const circleHandlerFactory = {
|
|
|
2419
3174
|
if (!defaults2) return;
|
|
2420
3175
|
const preview = getPreview(clampedPos);
|
|
2421
3176
|
if (preview) {
|
|
3177
|
+
const intensity = defaults2.cloudyBorderIntensity ?? 0;
|
|
3178
|
+
const pad = intensity > 0 ? getCloudyBorderExtent(intensity, defaults2.strokeWidth, true) : void 0;
|
|
2422
3179
|
const anno = {
|
|
2423
3180
|
...defaults2,
|
|
2424
3181
|
type: PdfAnnotationSubtype.CIRCLE,
|
|
@@ -2426,7 +3183,10 @@ const circleHandlerFactory = {
|
|
|
2426
3183
|
created: /* @__PURE__ */ new Date(),
|
|
2427
3184
|
id: uuidV4(),
|
|
2428
3185
|
pageIndex,
|
|
2429
|
-
rect: preview.data.rect
|
|
3186
|
+
rect: preview.data.rect,
|
|
3187
|
+
...pad !== void 0 && {
|
|
3188
|
+
rectangleDifferences: { left: pad, top: pad, right: pad, bottom: pad }
|
|
3189
|
+
}
|
|
2430
3190
|
};
|
|
2431
3191
|
onCommit(anno);
|
|
2432
3192
|
}
|
|
@@ -2453,6 +3213,135 @@ const circleHandlerFactory = {
|
|
|
2453
3213
|
};
|
|
2454
3214
|
}
|
|
2455
3215
|
};
|
|
3216
|
+
const textMarkupSelectionHandler = {
|
|
3217
|
+
toolId: "__textMarkup__",
|
|
3218
|
+
handle(context, selections, getText) {
|
|
3219
|
+
const tool = context.getTool();
|
|
3220
|
+
if (!tool) return;
|
|
3221
|
+
for (const selection of selections) {
|
|
3222
|
+
const id = uuidV4();
|
|
3223
|
+
getText().then((text) => {
|
|
3224
|
+
var _a;
|
|
3225
|
+
context.createAnnotation(selection.pageIndex, {
|
|
3226
|
+
...tool.defaults,
|
|
3227
|
+
rect: selection.rect,
|
|
3228
|
+
segmentRects: selection.segmentRects,
|
|
3229
|
+
pageIndex: selection.pageIndex,
|
|
3230
|
+
created: /* @__PURE__ */ new Date(),
|
|
3231
|
+
id,
|
|
3232
|
+
...text != null && { custom: { text } }
|
|
3233
|
+
});
|
|
3234
|
+
if ((_a = tool.behavior) == null ? void 0 : _a.selectAfterCreate) {
|
|
3235
|
+
context.selectAnnotation(selection.pageIndex, id);
|
|
3236
|
+
}
|
|
3237
|
+
});
|
|
3238
|
+
}
|
|
3239
|
+
}
|
|
3240
|
+
};
|
|
3241
|
+
function computeCaretRect(lastSegRect) {
|
|
3242
|
+
const lineHeight = lastSegRect.size.height;
|
|
3243
|
+
const height = lineHeight / 2;
|
|
3244
|
+
const width = height;
|
|
3245
|
+
const lineEndX = lastSegRect.origin.x + lastSegRect.size.width;
|
|
3246
|
+
return {
|
|
3247
|
+
origin: {
|
|
3248
|
+
x: lineEndX - width / 2,
|
|
3249
|
+
y: lastSegRect.origin.y + lineHeight / 2
|
|
3250
|
+
},
|
|
3251
|
+
size: { width, height }
|
|
3252
|
+
};
|
|
3253
|
+
}
|
|
3254
|
+
const insertTextSelectionHandler = {
|
|
3255
|
+
toolId: "insertText",
|
|
3256
|
+
handle(context, selections, getText) {
|
|
3257
|
+
const tool = context.getTool();
|
|
3258
|
+
if (!tool) return;
|
|
3259
|
+
const getDefaults = () => ({
|
|
3260
|
+
strokeColor: tool.defaults.strokeColor ?? "#E44234",
|
|
3261
|
+
opacity: tool.defaults.opacity ?? 1,
|
|
3262
|
+
flags: tool.defaults.flags ?? ["print"]
|
|
3263
|
+
});
|
|
3264
|
+
for (const selection of selections) {
|
|
3265
|
+
const lastSegRect = selection.segmentRects[selection.segmentRects.length - 1];
|
|
3266
|
+
if (!lastSegRect) continue;
|
|
3267
|
+
const caretRect = computeCaretRect(lastSegRect);
|
|
3268
|
+
const caretId = uuidV4();
|
|
3269
|
+
const defaults = getDefaults();
|
|
3270
|
+
getText().then((text) => {
|
|
3271
|
+
var _a;
|
|
3272
|
+
context.createAnnotation(selection.pageIndex, {
|
|
3273
|
+
type: PdfAnnotationSubtype.CARET,
|
|
3274
|
+
id: caretId,
|
|
3275
|
+
pageIndex: selection.pageIndex,
|
|
3276
|
+
rect: caretRect,
|
|
3277
|
+
strokeColor: defaults.strokeColor,
|
|
3278
|
+
opacity: defaults.opacity,
|
|
3279
|
+
intent: "Insert",
|
|
3280
|
+
rectangleDifferences: { left: 0.5, top: 0.5, right: 0.5, bottom: 0.5 },
|
|
3281
|
+
created: /* @__PURE__ */ new Date(),
|
|
3282
|
+
flags: defaults.flags,
|
|
3283
|
+
...text != null && { custom: { text } }
|
|
3284
|
+
});
|
|
3285
|
+
if ((_a = tool.behavior) == null ? void 0 : _a.selectAfterCreate) {
|
|
3286
|
+
context.selectAnnotation(selection.pageIndex, caretId);
|
|
3287
|
+
}
|
|
3288
|
+
});
|
|
3289
|
+
}
|
|
3290
|
+
}
|
|
3291
|
+
};
|
|
3292
|
+
const replaceTextSelectionHandler = {
|
|
3293
|
+
toolId: "replaceText",
|
|
3294
|
+
handle(context, selections, getText) {
|
|
3295
|
+
const tool = context.getTool();
|
|
3296
|
+
if (!tool) return;
|
|
3297
|
+
const getDefaults = () => ({
|
|
3298
|
+
strokeColor: tool.defaults.strokeColor ?? "#E44234",
|
|
3299
|
+
opacity: tool.defaults.opacity ?? 1,
|
|
3300
|
+
flags: tool.defaults.flags ?? ["print"]
|
|
3301
|
+
});
|
|
3302
|
+
for (const selection of selections) {
|
|
3303
|
+
const lastSegRect = selection.segmentRects[selection.segmentRects.length - 1];
|
|
3304
|
+
if (!lastSegRect) continue;
|
|
3305
|
+
const caretRect = computeCaretRect(lastSegRect);
|
|
3306
|
+
const caretId = uuidV4();
|
|
3307
|
+
const strikeoutId = uuidV4();
|
|
3308
|
+
const defaults = getDefaults();
|
|
3309
|
+
getText().then((text) => {
|
|
3310
|
+
var _a;
|
|
3311
|
+
context.createAnnotation(selection.pageIndex, {
|
|
3312
|
+
type: PdfAnnotationSubtype.CARET,
|
|
3313
|
+
id: caretId,
|
|
3314
|
+
pageIndex: selection.pageIndex,
|
|
3315
|
+
rect: caretRect,
|
|
3316
|
+
strokeColor: defaults.strokeColor,
|
|
3317
|
+
opacity: defaults.opacity,
|
|
3318
|
+
intent: "Replace",
|
|
3319
|
+
rectangleDifferences: { left: 0.5, top: 0.5, right: 0.5, bottom: 0.5 },
|
|
3320
|
+
created: /* @__PURE__ */ new Date(),
|
|
3321
|
+
flags: defaults.flags
|
|
3322
|
+
});
|
|
3323
|
+
context.createAnnotation(selection.pageIndex, {
|
|
3324
|
+
type: PdfAnnotationSubtype.STRIKEOUT,
|
|
3325
|
+
id: strikeoutId,
|
|
3326
|
+
pageIndex: selection.pageIndex,
|
|
3327
|
+
rect: selection.rect,
|
|
3328
|
+
segmentRects: selection.segmentRects,
|
|
3329
|
+
strokeColor: defaults.strokeColor,
|
|
3330
|
+
opacity: defaults.opacity,
|
|
3331
|
+
intent: "StrikeOutTextEdit",
|
|
3332
|
+
inReplyToId: caretId,
|
|
3333
|
+
replyType: PdfAnnotationReplyType.Group,
|
|
3334
|
+
created: /* @__PURE__ */ new Date(),
|
|
3335
|
+
flags: defaults.flags,
|
|
3336
|
+
...text != null && { custom: { text } }
|
|
3337
|
+
});
|
|
3338
|
+
if ((_a = tool.behavior) == null ? void 0 : _a.selectAfterCreate) {
|
|
3339
|
+
context.selectAnnotation(selection.pageIndex, caretId);
|
|
3340
|
+
}
|
|
3341
|
+
});
|
|
3342
|
+
}
|
|
3343
|
+
}
|
|
3344
|
+
};
|
|
2456
3345
|
const patchInk = (original, ctx) => {
|
|
2457
3346
|
switch (ctx.type) {
|
|
2458
3347
|
case "vertex-edit":
|
|
@@ -2469,23 +3358,29 @@ const patchInk = (original, ctx) => {
|
|
|
2469
3358
|
}
|
|
2470
3359
|
case "resize": {
|
|
2471
3360
|
if (!ctx.changes.rect) return ctx.changes;
|
|
2472
|
-
const { oldRect, resolvedRect, rects } = baseResizeScaling(
|
|
3361
|
+
const { scaleX, scaleY, oldRect, resolvedRect, rects } = baseResizeScaling(
|
|
2473
3362
|
original,
|
|
2474
3363
|
ctx.changes.rect,
|
|
2475
3364
|
ctx.metadata
|
|
2476
3365
|
);
|
|
2477
|
-
const inset = (
|
|
2478
|
-
origin: { x:
|
|
3366
|
+
const inset = (r2, pad) => ({
|
|
3367
|
+
origin: { x: r2.origin.x + pad, y: r2.origin.y + pad },
|
|
2479
3368
|
size: {
|
|
2480
|
-
width: Math.max(1,
|
|
2481
|
-
height: Math.max(1,
|
|
3369
|
+
width: Math.max(1, r2.size.width - pad * 2),
|
|
3370
|
+
height: Math.max(1, r2.size.height - pad * 2)
|
|
2482
3371
|
}
|
|
2483
3372
|
});
|
|
2484
|
-
const
|
|
2485
|
-
|
|
2486
|
-
|
|
3373
|
+
const resizeEpsilon = 1e-3;
|
|
3374
|
+
const widthChanged = Math.abs(scaleX - 1) > resizeEpsilon;
|
|
3375
|
+
const heightChanged = Math.abs(scaleY - 1) > resizeEpsilon;
|
|
3376
|
+
const strokeScale = widthChanged && !heightChanged ? scaleX : !widthChanged && heightChanged ? scaleY : Math.min(scaleX, scaleY);
|
|
3377
|
+
const rawStrokeWidth = Math.max(1, original.strokeWidth * strokeScale);
|
|
3378
|
+
const maxStrokeWidth = Math.max(
|
|
3379
|
+
1,
|
|
3380
|
+
Math.min(resolvedRect.size.width, resolvedRect.size.height)
|
|
2487
3381
|
);
|
|
2488
|
-
const
|
|
3382
|
+
const clampedStrokeWidth = Math.min(rawStrokeWidth, maxStrokeWidth);
|
|
3383
|
+
const newStrokeWidth = Number(clampedStrokeWidth.toFixed(1));
|
|
2489
3384
|
const innerOld = inset(oldRect, original.strokeWidth / 2);
|
|
2490
3385
|
const innerNew = inset(resolvedRect, newStrokeWidth / 2);
|
|
2491
3386
|
const sx = innerNew.size.width / Math.max(innerOld.size.width, 1e-6);
|
|
@@ -2695,11 +3590,17 @@ const patchPolyline = (orig, ctx) => {
|
|
|
2695
3590
|
return ctx.changes;
|
|
2696
3591
|
}
|
|
2697
3592
|
};
|
|
3593
|
+
function getPolygonPad(intensity, strokeWidth) {
|
|
3594
|
+
if ((intensity ?? 0) > 0) {
|
|
3595
|
+
return getCloudyBorderExtent(intensity, strokeWidth, false);
|
|
3596
|
+
}
|
|
3597
|
+
return strokeWidth / 2;
|
|
3598
|
+
}
|
|
2698
3599
|
const patchPolygon = (orig, ctx) => {
|
|
2699
3600
|
switch (ctx.type) {
|
|
2700
3601
|
case "vertex-edit":
|
|
2701
3602
|
if (ctx.changes.vertices && ctx.changes.vertices.length) {
|
|
2702
|
-
const pad = orig.strokeWidth
|
|
3603
|
+
const pad = getPolygonPad(orig.cloudyBorderIntensity, orig.strokeWidth);
|
|
2703
3604
|
const rawVertices = ctx.changes.vertices;
|
|
2704
3605
|
const rawRect = expandRect(rectFromPoints(rawVertices), pad);
|
|
2705
3606
|
const compensated = compensateRotatedVertexEdit(orig, rawVertices, rawRect);
|
|
@@ -2743,15 +3644,30 @@ const patchPolygon = (orig, ctx) => {
|
|
|
2743
3644
|
};
|
|
2744
3645
|
}
|
|
2745
3646
|
case "property-update": {
|
|
2746
|
-
const
|
|
3647
|
+
const cloudyChanged = ctx.changes.cloudyBorderIntensity !== void 0;
|
|
3648
|
+
const needsRectUpdate = ctx.changes.strokeWidth !== void 0 || ctx.changes.rotation !== void 0 || cloudyChanged;
|
|
2747
3649
|
if (!needsRectUpdate) return ctx.changes;
|
|
2748
3650
|
const merged = { ...orig, ...ctx.changes };
|
|
2749
|
-
const pad = merged.strokeWidth
|
|
3651
|
+
const pad = getPolygonPad(merged.cloudyBorderIntensity, merged.strokeWidth);
|
|
2750
3652
|
const tightRect = expandRect(rectFromPoints(merged.vertices), pad);
|
|
3653
|
+
let patch = ctx.changes;
|
|
3654
|
+
const hasCloudy = (orig.cloudyBorderIntensity ?? 0) > 0;
|
|
3655
|
+
if (cloudyChanged || ctx.changes.strokeWidth !== void 0 && hasCloudy) {
|
|
3656
|
+
const intensity = merged.cloudyBorderIntensity ?? 0;
|
|
3657
|
+
if (intensity > 0) {
|
|
3658
|
+
const extent = getCloudyBorderExtent(intensity, merged.strokeWidth, false);
|
|
3659
|
+
patch = {
|
|
3660
|
+
...patch,
|
|
3661
|
+
rectangleDifferences: { left: extent, top: extent, right: extent, bottom: extent }
|
|
3662
|
+
};
|
|
3663
|
+
} else {
|
|
3664
|
+
patch = { ...patch, rectangleDifferences: void 0 };
|
|
3665
|
+
}
|
|
3666
|
+
}
|
|
2751
3667
|
const effectiveRotation = ctx.changes.rotation ?? orig.rotation ?? 0;
|
|
2752
3668
|
if (orig.unrotatedRect || ctx.changes.rotation !== void 0) {
|
|
2753
3669
|
return {
|
|
2754
|
-
...
|
|
3670
|
+
...patch,
|
|
2755
3671
|
unrotatedRect: tightRect,
|
|
2756
3672
|
rect: calculateRotatedRectAABBAroundPoint(
|
|
2757
3673
|
tightRect,
|
|
@@ -2760,7 +3676,7 @@ const patchPolygon = (orig, ctx) => {
|
|
|
2760
3676
|
)
|
|
2761
3677
|
};
|
|
2762
3678
|
}
|
|
2763
|
-
return { ...
|
|
3679
|
+
return { ...patch, rect: tightRect };
|
|
2764
3680
|
}
|
|
2765
3681
|
default:
|
|
2766
3682
|
return ctx.changes;
|
|
@@ -2776,11 +3692,29 @@ const patchCircle = (orig, ctx) => {
|
|
|
2776
3692
|
return baseResizeScaling(orig, ctx.changes.rect, ctx.metadata).rects;
|
|
2777
3693
|
case "rotate":
|
|
2778
3694
|
return baseRotateChanges(orig, ctx) ?? ctx.changes;
|
|
2779
|
-
case "property-update":
|
|
3695
|
+
case "property-update": {
|
|
3696
|
+
let patch = ctx.changes;
|
|
3697
|
+
const cloudyChanged = ctx.changes.cloudyBorderIntensity !== void 0;
|
|
3698
|
+
const strokeChanged = ctx.changes.strokeWidth !== void 0;
|
|
3699
|
+
const hasCloudy = (orig.cloudyBorderIntensity ?? 0) > 0;
|
|
3700
|
+
if (cloudyChanged || strokeChanged && hasCloudy) {
|
|
3701
|
+
const merged = { ...orig, ...ctx.changes };
|
|
3702
|
+
const intensity = merged.cloudyBorderIntensity ?? 0;
|
|
3703
|
+
if (intensity > 0) {
|
|
3704
|
+
const extent = getCloudyBorderExtent(intensity, merged.strokeWidth, true);
|
|
3705
|
+
patch = {
|
|
3706
|
+
...patch,
|
|
3707
|
+
rectangleDifferences: { left: extent, top: extent, right: extent, bottom: extent }
|
|
3708
|
+
};
|
|
3709
|
+
} else {
|
|
3710
|
+
patch = { ...patch, rectangleDifferences: void 0 };
|
|
3711
|
+
}
|
|
3712
|
+
}
|
|
2780
3713
|
if (ctx.changes.rotation !== void 0) {
|
|
2781
|
-
|
|
3714
|
+
patch = { ...patch, ...basePropertyRotationChanges(orig, ctx.changes.rotation) };
|
|
2782
3715
|
}
|
|
2783
|
-
return
|
|
3716
|
+
return patch;
|
|
3717
|
+
}
|
|
2784
3718
|
default:
|
|
2785
3719
|
return ctx.changes;
|
|
2786
3720
|
}
|
|
@@ -2795,11 +3729,29 @@ const patchSquare = (orig, ctx) => {
|
|
|
2795
3729
|
return baseResizeScaling(orig, ctx.changes.rect, ctx.metadata).rects;
|
|
2796
3730
|
case "rotate":
|
|
2797
3731
|
return baseRotateChanges(orig, ctx) ?? ctx.changes;
|
|
2798
|
-
case "property-update":
|
|
3732
|
+
case "property-update": {
|
|
3733
|
+
let patch = ctx.changes;
|
|
3734
|
+
const cloudyChanged = ctx.changes.cloudyBorderIntensity !== void 0;
|
|
3735
|
+
const strokeChanged = ctx.changes.strokeWidth !== void 0;
|
|
3736
|
+
const hasCloudy = (orig.cloudyBorderIntensity ?? 0) > 0;
|
|
3737
|
+
if (cloudyChanged || strokeChanged && hasCloudy) {
|
|
3738
|
+
const merged = { ...orig, ...ctx.changes };
|
|
3739
|
+
const intensity = merged.cloudyBorderIntensity ?? 0;
|
|
3740
|
+
if (intensity > 0) {
|
|
3741
|
+
const extent = getCloudyBorderExtent(intensity, merged.strokeWidth, false);
|
|
3742
|
+
patch = {
|
|
3743
|
+
...patch,
|
|
3744
|
+
rectangleDifferences: { left: extent, top: extent, right: extent, bottom: extent }
|
|
3745
|
+
};
|
|
3746
|
+
} else {
|
|
3747
|
+
patch = { ...patch, rectangleDifferences: void 0 };
|
|
3748
|
+
}
|
|
3749
|
+
}
|
|
2799
3750
|
if (ctx.changes.rotation !== void 0) {
|
|
2800
|
-
|
|
3751
|
+
patch = { ...patch, ...basePropertyRotationChanges(orig, ctx.changes.rotation) };
|
|
2801
3752
|
}
|
|
2802
|
-
return
|
|
3753
|
+
return patch;
|
|
3754
|
+
}
|
|
2803
3755
|
default:
|
|
2804
3756
|
return ctx.changes;
|
|
2805
3757
|
}
|
|
@@ -2842,34 +3794,6 @@ const patchStamp = (orig, ctx) => {
|
|
|
2842
3794
|
return ctx.changes;
|
|
2843
3795
|
}
|
|
2844
3796
|
};
|
|
2845
|
-
function convertAABBRectToUnrotatedSpace(newAABBRect, originalAABBRect, originalUnrotatedRect, rotationDegrees) {
|
|
2846
|
-
const theta = rotationDegrees * Math.PI / 180;
|
|
2847
|
-
const A = Math.abs(Math.cos(theta));
|
|
2848
|
-
const B = Math.abs(Math.sin(theta));
|
|
2849
|
-
const det = A * A - B * B;
|
|
2850
|
-
const newAABBw = newAABBRect.size.width;
|
|
2851
|
-
const newAABBh = newAABBRect.size.height;
|
|
2852
|
-
let newWidth;
|
|
2853
|
-
let newHeight;
|
|
2854
|
-
if (Math.abs(det) > 1e-6) {
|
|
2855
|
-
newWidth = (A * newAABBw - B * newAABBh) / det;
|
|
2856
|
-
newHeight = (A * newAABBh - B * newAABBw) / det;
|
|
2857
|
-
newWidth = Math.max(newWidth, 1);
|
|
2858
|
-
newHeight = Math.max(newHeight, 1);
|
|
2859
|
-
} else {
|
|
2860
|
-
const origArea = originalAABBRect.size.width * originalAABBRect.size.height;
|
|
2861
|
-
const newArea = newAABBw * newAABBh;
|
|
2862
|
-
const uniformScale = origArea > 0 ? Math.sqrt(newArea / origArea) : 1;
|
|
2863
|
-
newWidth = originalUnrotatedRect.size.width * uniformScale;
|
|
2864
|
-
newHeight = originalUnrotatedRect.size.height * uniformScale;
|
|
2865
|
-
}
|
|
2866
|
-
const newCenterX = newAABBRect.origin.x + newAABBw / 2;
|
|
2867
|
-
const newCenterY = newAABBRect.origin.y + newAABBh / 2;
|
|
2868
|
-
return {
|
|
2869
|
-
origin: { x: newCenterX - newWidth / 2, y: newCenterY - newHeight / 2 },
|
|
2870
|
-
size: { width: newWidth, height: newHeight }
|
|
2871
|
-
};
|
|
2872
|
-
}
|
|
2873
3797
|
const _AnnotationPlugin = class _AnnotationPlugin extends BasePlugin {
|
|
2874
3798
|
constructor(id, registry, config) {
|
|
2875
3799
|
var _a, _b, _c;
|
|
@@ -2881,6 +3805,7 @@ const _AnnotationPlugin = class _AnnotationPlugin extends BasePlugin {
|
|
|
2881
3805
|
this.importQueue = /* @__PURE__ */ new Map();
|
|
2882
3806
|
this.commitInProgress = /* @__PURE__ */ new Map();
|
|
2883
3807
|
this.handlerFactories = /* @__PURE__ */ new Map();
|
|
3808
|
+
this.selectionHandlerFactories = /* @__PURE__ */ new Map();
|
|
2884
3809
|
this.activeTool$ = createBehaviorEmitter();
|
|
2885
3810
|
this.events$ = createBehaviorEmitter();
|
|
2886
3811
|
this.toolsChange$ = createBehaviorEmitter();
|
|
@@ -2897,6 +3822,7 @@ const _AnnotationPlugin = class _AnnotationPlugin extends BasePlugin {
|
|
|
2897
3822
|
this.history = ((_b = registry.getPlugin("history")) == null ? void 0 : _b.provides()) ?? null;
|
|
2898
3823
|
this.interactionManager = ((_c = registry.getPlugin("interaction-manager")) == null ? void 0 : _c.provides()) ?? null;
|
|
2899
3824
|
this.registerHandlerFactories();
|
|
3825
|
+
this.registerSelectionHandlerFactories();
|
|
2900
3826
|
this.registerBuiltInPatches();
|
|
2901
3827
|
}
|
|
2902
3828
|
// ─────────────────────────────────────────────────────────
|
|
@@ -2922,7 +3848,7 @@ const _AnnotationPlugin = class _AnnotationPlugin extends BasePlugin {
|
|
|
2922
3848
|
for (const tool of this.state.tools) {
|
|
2923
3849
|
if (tool.interaction.textSelection) {
|
|
2924
3850
|
this.selection.enableForMode(tool.interaction.mode ?? tool.id, {
|
|
2925
|
-
showSelectionRects: false,
|
|
3851
|
+
showSelectionRects: tool.interaction.showSelectionRects ?? false,
|
|
2926
3852
|
enableSelection: true,
|
|
2927
3853
|
enableMarquee: false
|
|
2928
3854
|
});
|
|
@@ -2951,6 +3877,11 @@ const _AnnotationPlugin = class _AnnotationPlugin extends BasePlugin {
|
|
|
2951
3877
|
this.handlerFactories.set(PdfAnnotationSubtype.LINE, lineHandlerFactory);
|
|
2952
3878
|
this.handlerFactories.set(PdfAnnotationSubtype.INK, inkHandlerFactory);
|
|
2953
3879
|
this.handlerFactories.set(PdfAnnotationSubtype.FREETEXT, freeTextHandlerFactory);
|
|
3880
|
+
this.handlerFactories.set(PdfAnnotationSubtype.TEXT, textHandlerFactory);
|
|
3881
|
+
}
|
|
3882
|
+
registerSelectionHandlerFactories() {
|
|
3883
|
+
this.selectionHandlerFactories.set("insertText", insertTextSelectionHandler);
|
|
3884
|
+
this.selectionHandlerFactories.set("replaceText", replaceTextSelectionHandler);
|
|
2954
3885
|
}
|
|
2955
3886
|
registerBuiltInPatches() {
|
|
2956
3887
|
this.patchRegistry.register(PdfAnnotationSubtype.INK, patchInk);
|
|
@@ -2984,7 +3915,7 @@ const _AnnotationPlugin = class _AnnotationPlugin extends BasePlugin {
|
|
|
2984
3915
|
if (modeId !== "pointerMode") return;
|
|
2985
3916
|
const docState = this.state.documents[documentId];
|
|
2986
3917
|
if (!docState) return;
|
|
2987
|
-
const pageAnnotations = (docState.pages[pageIndex] ?? []).map((uid) => docState.byUid[uid]).filter((ta) => ta !== void 0).filter((ta) =>
|
|
3918
|
+
const pageAnnotations = (docState.pages[pageIndex] ?? []).map((uid) => docState.byUid[uid]).filter((ta) => ta !== void 0).filter((ta) => isSidebarAnnotation(ta));
|
|
2988
3919
|
const selectedIds = pageAnnotations.filter((ta) => rectsIntersect(rect, ta.object.rect)).map((ta) => ta.object.id);
|
|
2989
3920
|
if (selectedIds.length > 0) {
|
|
2990
3921
|
const expandedIds = /* @__PURE__ */ new Set();
|
|
@@ -3002,44 +3933,32 @@ const _AnnotationPlugin = class _AnnotationPlugin extends BasePlugin {
|
|
|
3002
3933
|
}
|
|
3003
3934
|
});
|
|
3004
3935
|
(_c = this.selection) == null ? void 0 : _c.onEndSelection(({ documentId }) => {
|
|
3005
|
-
var _a2, _b2, _c2;
|
|
3006
|
-
if (!this.checkPermission(documentId, PdfPermissionFlag.ModifyAnnotations))
|
|
3007
|
-
return;
|
|
3008
|
-
}
|
|
3936
|
+
var _a2, _b2, _c2, _d;
|
|
3937
|
+
if (!this.checkPermission(documentId, PdfPermissionFlag.ModifyAnnotations)) return;
|
|
3009
3938
|
const activeTool = this.getActiveTool(documentId);
|
|
3010
3939
|
if (!activeTool || !activeTool.interaction.textSelection) return;
|
|
3011
3940
|
const formattedSelection = (_a2 = this.selection) == null ? void 0 : _a2.getFormattedSelection();
|
|
3012
3941
|
const selectionText = (_b2 = this.selection) == null ? void 0 : _b2.getSelectedText();
|
|
3013
3942
|
if (!formattedSelection || !selectionText) return;
|
|
3014
|
-
|
|
3015
|
-
selectionText.wait(
|
|
3016
|
-
|
|
3017
|
-
|
|
3018
|
-
|
|
3019
|
-
|
|
3020
|
-
|
|
3021
|
-
|
|
3022
|
-
|
|
3023
|
-
|
|
3024
|
-
|
|
3025
|
-
|
|
3026
|
-
|
|
3027
|
-
|
|
3028
|
-
|
|
3029
|
-
|
|
3030
|
-
|
|
3031
|
-
void 0,
|
|
3032
|
-
documentId
|
|
3033
|
-
);
|
|
3034
|
-
if ((_a3 = activeTool.behavior) == null ? void 0 : _a3.deactivateToolAfterCreate) {
|
|
3035
|
-
this.setActiveTool(null, documentId);
|
|
3036
|
-
}
|
|
3037
|
-
if ((_b3 = activeTool.behavior) == null ? void 0 : _b3.selectAfterCreate) {
|
|
3038
|
-
this.selectAnnotation(selection.pageIndex, annotationId, documentId);
|
|
3039
|
-
}
|
|
3040
|
-
}, ignore);
|
|
3943
|
+
const getText = () => new Promise((resolve) => {
|
|
3944
|
+
selectionText.wait(
|
|
3945
|
+
(text) => resolve(text.join("\n")),
|
|
3946
|
+
() => resolve(void 0)
|
|
3947
|
+
);
|
|
3948
|
+
});
|
|
3949
|
+
const context = {
|
|
3950
|
+
toolId: activeTool.id,
|
|
3951
|
+
documentId,
|
|
3952
|
+
getTool: () => this.getActiveTool(documentId) ?? null,
|
|
3953
|
+
createAnnotation: (pageIndex, annotation) => this.createAnnotation(pageIndex, annotation, void 0, documentId),
|
|
3954
|
+
selectAnnotation: (pageIndex, id) => this.selectAnnotation(pageIndex, id, documentId)
|
|
3955
|
+
};
|
|
3956
|
+
const handler = this.selectionHandlerFactories.get(activeTool.id) ?? textMarkupSelectionHandler;
|
|
3957
|
+
handler.handle(context, formattedSelection, getText);
|
|
3958
|
+
if ((_c2 = activeTool.behavior) == null ? void 0 : _c2.deactivateToolAfterCreate) {
|
|
3959
|
+
this.setActiveTool(null, documentId);
|
|
3041
3960
|
}
|
|
3042
|
-
(
|
|
3961
|
+
(_d = this.selection) == null ? void 0 : _d.clear();
|
|
3043
3962
|
});
|
|
3044
3963
|
}
|
|
3045
3964
|
registerInteractionForTool(tool) {
|
|
@@ -4867,6 +5786,8 @@ const isPolylineTool = createToolPredicate("polyline");
|
|
|
4867
5786
|
const isPolygonTool = createToolPredicate("polygon");
|
|
4868
5787
|
const isFreeTextTool = createToolPredicate("freeText");
|
|
4869
5788
|
const isStampTool = createToolPredicate("stamp");
|
|
5789
|
+
const isInsertTextTool = createToolPredicate("insertText");
|
|
5790
|
+
const isReplaceTextTool = createToolPredicate("replaceText");
|
|
4870
5791
|
const AnnotationPluginPackage = {
|
|
4871
5792
|
manifest,
|
|
4872
5793
|
create: (registry, config) => new AnnotationPlugin(ANNOTATION_PLUGIN_ID, registry, config),
|
|
@@ -4881,10 +5802,14 @@ export {
|
|
|
4881
5802
|
calculateRotatedRectAABBAroundPoint2 as calculateRotatedRectAABBAroundPoint,
|
|
4882
5803
|
convertAABBRectToUnrotatedSpace,
|
|
4883
5804
|
createToolPredicate,
|
|
5805
|
+
generateCloudyEllipsePath,
|
|
5806
|
+
generateCloudyPolygonPath,
|
|
5807
|
+
generateCloudyRectanglePath,
|
|
4884
5808
|
getAnnotationByUid,
|
|
4885
5809
|
getAnnotations,
|
|
4886
5810
|
getAnnotationsByPageIndex,
|
|
4887
5811
|
getAttachedLinks,
|
|
5812
|
+
getCloudyBorderExtent,
|
|
4888
5813
|
getGroupLeaderId,
|
|
4889
5814
|
getGroupMembers,
|
|
4890
5815
|
getIRTChildIds,
|
|
@@ -4906,6 +5831,7 @@ export {
|
|
|
4906
5831
|
initialDocumentState,
|
|
4907
5832
|
initialState,
|
|
4908
5833
|
isAnnotationSelected,
|
|
5834
|
+
isCaret,
|
|
4909
5835
|
isCircle,
|
|
4910
5836
|
isCircleTool,
|
|
4911
5837
|
isFreeText,
|
|
@@ -4916,6 +5842,7 @@ export {
|
|
|
4916
5842
|
isInk,
|
|
4917
5843
|
isInkHighlighterTool,
|
|
4918
5844
|
isInkTool,
|
|
5845
|
+
isInsertTextTool,
|
|
4919
5846
|
isLine,
|
|
4920
5847
|
isLineTool,
|
|
4921
5848
|
isLink,
|
|
@@ -4924,6 +5851,7 @@ export {
|
|
|
4924
5851
|
isPolyline,
|
|
4925
5852
|
isPolylineTool,
|
|
4926
5853
|
isRedact,
|
|
5854
|
+
isReplaceTextTool,
|
|
4927
5855
|
isSidebarAnnotation,
|
|
4928
5856
|
isSquare,
|
|
4929
5857
|
isSquareTool,
|