@muhammara/native-core 7.0.0-beta.2 → 7.0.0-beta.4
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/lib/Recipe.js +7 -2
- package/lib/recipe/annotation.js +19 -5
- package/lib/recipe/appendPage.js +6 -1
- package/lib/recipe/htmlToTextObjects.js +37 -1
- package/lib/recipe/page.js +16 -13
- package/lib/recipe/parameters.js +3 -1
- package/lib/recipe/table.js +223 -169
- package/lib/recipe/text.helper.js +4 -2
- package/lib/recipe/text.js +130 -63
- package/lib/recipe/utils.js +52 -0
- package/lib/recipe/vector.helper.js +2 -7
- package/lib/recipe/vector.js +0 -2
- package/muhammara.d.ts +29 -8
- package/package.json +1 -1
package/lib/recipe/text.js
CHANGED
|
@@ -379,7 +379,6 @@ exports.text = function text(text = "", x, y, options = {}) {
|
|
|
379
379
|
let columnIndex = 1;
|
|
380
380
|
|
|
381
381
|
toWriteTextObjects.some((toWriteTextObject, index) => {
|
|
382
|
-
const isHTML = toWriteTextObject.writeOptions.html;
|
|
383
382
|
const { text, lineHeight, lineWidth, lineID, spaceWidth } =
|
|
384
383
|
toWriteTextObject;
|
|
385
384
|
|
|
@@ -484,6 +483,9 @@ exports.text = function text(text = "", x, y, options = {}) {
|
|
|
484
483
|
// nothing to write, so simply escape.
|
|
485
484
|
return next_x;
|
|
486
485
|
}
|
|
486
|
+
// The opacity and rotation path below resets x to the text box edge;
|
|
487
|
+
// text-markup annotations need the line's own start.
|
|
488
|
+
const lineX = x;
|
|
487
489
|
|
|
488
490
|
if (options.underline || options.strikeOut) {
|
|
489
491
|
options.lineWidth = lineWidth;
|
|
@@ -607,18 +609,38 @@ exports.text = function text(text = "", x, y, options = {}) {
|
|
|
607
609
|
|
|
608
610
|
const { textHeight } = options;
|
|
609
611
|
|
|
612
|
+
var markupLeft = lineX;
|
|
613
|
+
var markupBottom = y - textHeight * 0.2;
|
|
614
|
+
var markupWidth = _justify ? next_x - lineX : currentLineWidth;
|
|
615
|
+
var markupHeight = textHeight * 1.4;
|
|
616
|
+
if (textBox.wrap === "clip") {
|
|
617
|
+
// Clipped runs retain the first overflowing word, so measure the
|
|
618
|
+
// drawn text instead of using the preceding fitting line's width.
|
|
619
|
+
var markupRight = Math.min(
|
|
620
|
+
lineX + new Word(text, options).dimensions.xMax,
|
|
621
|
+
nx + textBox.width,
|
|
622
|
+
);
|
|
623
|
+
var markupTop = Math.min(markupBottom + markupHeight, y + lineHeight);
|
|
624
|
+
markupLeft = Math.max(markupLeft, nx);
|
|
625
|
+
markupBottom = Math.max(markupBottom, y);
|
|
626
|
+
markupWidth = markupRight - markupLeft;
|
|
627
|
+
markupHeight = markupTop - markupBottom;
|
|
628
|
+
if (markupWidth <= 0 || markupHeight <= 0) return next_x;
|
|
629
|
+
}
|
|
630
|
+
|
|
610
631
|
for (let key in targetAnnotations) {
|
|
611
632
|
const subtype = this._getTextMarkupAnnotationSubtype(key);
|
|
612
|
-
if (subtype) {
|
|
633
|
+
if (subtype && targetAnnotations[key]) {
|
|
634
|
+
// Copy so the caller's markup options are not modified.
|
|
613
635
|
const markupOption =
|
|
614
636
|
typeof targetAnnotations[key] != "object"
|
|
615
637
|
? {}
|
|
616
|
-
: targetAnnotations[key];
|
|
638
|
+
: { ...targetAnnotations[key] };
|
|
617
639
|
const { title, open, richText, flag, icon, date, subject } =
|
|
618
640
|
targetAnnotations;
|
|
619
641
|
Object.assign(markupOption, {
|
|
620
|
-
height:
|
|
621
|
-
width:
|
|
642
|
+
height: markupHeight,
|
|
643
|
+
width: markupWidth,
|
|
622
644
|
text: markupOption.text || "",
|
|
623
645
|
_textHeight: textHeight,
|
|
624
646
|
// add options to annotation
|
|
@@ -630,7 +652,10 @@ exports.text = function text(text = "", x, y, options = {}) {
|
|
|
630
652
|
date: date || "",
|
|
631
653
|
subject: subject || "",
|
|
632
654
|
});
|
|
633
|
-
const { ox, oy } = this._reverseCoordinate(
|
|
655
|
+
const { ox, oy } = this._reverseCoordinate(
|
|
656
|
+
markupLeft,
|
|
657
|
+
markupBottom,
|
|
658
|
+
);
|
|
634
659
|
|
|
635
660
|
this.annot(ox, oy, subtype, markupOption);
|
|
636
661
|
}
|
|
@@ -639,6 +664,29 @@ exports.text = function text(text = "", x, y, options = {}) {
|
|
|
639
664
|
return next_x;
|
|
640
665
|
};
|
|
641
666
|
|
|
667
|
+
/** Queues a text link limited to the run's visible clipping region. */
|
|
668
|
+
var queueTextLink = function (content, x, y, nextX) {
|
|
669
|
+
if (!content.writeOptions.link) return;
|
|
670
|
+
var left = x;
|
|
671
|
+
var width = nextX ? nextX - x : content.lineWidth;
|
|
672
|
+
if (textBox.wrap === "clip") {
|
|
673
|
+
var right = Math.min(
|
|
674
|
+
x + new Word(content.text, content.writeOptions).dimensions.xMax,
|
|
675
|
+
nx + textBox.width,
|
|
676
|
+
);
|
|
677
|
+
left = Math.max(left, nx);
|
|
678
|
+
width = right - left;
|
|
679
|
+
if (width <= 0) return;
|
|
680
|
+
}
|
|
681
|
+
linkAnnotations.push({
|
|
682
|
+
url: content.writeOptions.link,
|
|
683
|
+
left,
|
|
684
|
+
bottom: y,
|
|
685
|
+
width,
|
|
686
|
+
height: content.lineHeight,
|
|
687
|
+
});
|
|
688
|
+
};
|
|
689
|
+
|
|
642
690
|
if (!isContinued) {
|
|
643
691
|
// flush out current line before processing next one
|
|
644
692
|
let next_x = 0;
|
|
@@ -646,15 +694,7 @@ exports.text = function text(text = "", x, y, options = {}) {
|
|
|
646
694
|
const x = next_x || getStartX(content.startX, content);
|
|
647
695
|
const y = currentY;
|
|
648
696
|
next_x = writeText(context, x, y, content);
|
|
649
|
-
|
|
650
|
-
linkAnnotations.push({
|
|
651
|
-
url: content.writeOptions.link,
|
|
652
|
-
left: x,
|
|
653
|
-
bottom: y,
|
|
654
|
-
width: next_x ? next_x - x : content.lineWidth,
|
|
655
|
-
height: content.lineHeight,
|
|
656
|
-
});
|
|
657
|
-
}
|
|
697
|
+
queueTextLink(content, x, y, next_x);
|
|
658
698
|
});
|
|
659
699
|
// The line offset from the last line in the
|
|
660
700
|
// group determines Y positioning for next line.
|
|
@@ -683,6 +723,7 @@ exports.text = function text(text = "", x, y, options = {}) {
|
|
|
683
723
|
}
|
|
684
724
|
|
|
685
725
|
if (overflow && this._overflowNotifier) {
|
|
726
|
+
flushTextLinks(this, linkAnnotations);
|
|
686
727
|
let orders = this._overflowNotifier(this);
|
|
687
728
|
if (orders === true) {
|
|
688
729
|
return true; // stop processing remaining text.
|
|
@@ -723,18 +764,16 @@ exports.text = function text(text = "", x, y, options = {}) {
|
|
|
723
764
|
|
|
724
765
|
const startX = nx + currentLineWidth;
|
|
725
766
|
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
if (text != "[@@DONOT_RENDER_THIS@@]") {
|
|
729
|
-
toWriteTextObject.startX = toWriteTextObject.startX || startX;
|
|
730
|
-
toWriteContents.push(toWriteTextObject);
|
|
731
|
-
}
|
|
767
|
+
toWriteTextObject.startX = toWriteTextObject.startX || startX;
|
|
768
|
+
toWriteContents.push(toWriteTextObject);
|
|
732
769
|
|
|
733
770
|
// To handle text that has been split in middle of word,
|
|
734
|
-
// need to decide if current text ends with a space.
|
|
771
|
+
// need to decide if current text ends with a space. HTML segments carry
|
|
772
|
+
// their own separating space, so adding one unconditionally in HTML mode
|
|
773
|
+
// measured every line a space wider than it is drawn and pushed aligned
|
|
774
|
+
// lines left.
|
|
735
775
|
currentLineWidth +=
|
|
736
|
-
lineWidth +
|
|
737
|
-
(isHTML || toWriteTextObject.text.endsWith(" ") ? spaceWidth : 0);
|
|
776
|
+
lineWidth + (toWriteTextObject.text.endsWith(" ") ? spaceWidth : 0);
|
|
738
777
|
|
|
739
778
|
// Processing last text object?
|
|
740
779
|
if (index === toWriteTextObjects.length - 1) {
|
|
@@ -753,15 +792,7 @@ exports.text = function text(text = "", x, y, options = {}) {
|
|
|
753
792
|
const x = next_x || getStartX(content.startX, content);
|
|
754
793
|
const y = currentY;
|
|
755
794
|
next_x = writeText(context, x, y, content);
|
|
756
|
-
|
|
757
|
-
linkAnnotations.push({
|
|
758
|
-
url: content.writeOptions.link,
|
|
759
|
-
left: x,
|
|
760
|
-
bottom: y,
|
|
761
|
-
width: next_x ? next_x - x : content.lineWidth,
|
|
762
|
-
height: content.lineHeight,
|
|
763
|
-
});
|
|
764
|
-
}
|
|
795
|
+
queueTextLink(content, x, y, next_x);
|
|
765
796
|
}
|
|
766
797
|
|
|
767
798
|
// Flush any left over text objects.
|
|
@@ -772,23 +803,29 @@ exports.text = function text(text = "", x, y, options = {}) {
|
|
|
772
803
|
});
|
|
773
804
|
|
|
774
805
|
if (clipResult && typeof textBox.onClip === "function") {
|
|
775
|
-
// The
|
|
806
|
+
// The callback may finish the page or start drawing on another one.
|
|
807
|
+
flushTextLinks(this, linkAnnotations);
|
|
776
808
|
textBox.onClip(this, clipResult);
|
|
777
809
|
}
|
|
778
810
|
}
|
|
779
811
|
|
|
780
|
-
linkAnnotations
|
|
812
|
+
flushTextLinks(this, linkAnnotations);
|
|
813
|
+
return this;
|
|
814
|
+
};
|
|
815
|
+
|
|
816
|
+
/** Writes pending text links before a callback can change the active page. @private */
|
|
817
|
+
function flushTextLinks(recipe, annotations) {
|
|
818
|
+
for (var annotation of annotations.splice(0)) {
|
|
781
819
|
linkPdf(
|
|
782
|
-
|
|
820
|
+
recipe,
|
|
783
821
|
annotation.url,
|
|
784
822
|
annotation.left,
|
|
785
823
|
annotation.bottom,
|
|
786
824
|
annotation.width,
|
|
787
825
|
annotation.height,
|
|
788
826
|
);
|
|
789
|
-
}
|
|
790
|
-
|
|
791
|
-
};
|
|
827
|
+
}
|
|
828
|
+
}
|
|
792
829
|
|
|
793
830
|
exports._layoutText = function _layoutText(textObjects, textBox, pathOptions) {
|
|
794
831
|
let totalHeight = 0;
|
|
@@ -831,19 +868,52 @@ exports._layoutText = function _layoutText(textObjects, textBox, pathOptions) {
|
|
|
831
868
|
|
|
832
869
|
let firstLineHeight;
|
|
833
870
|
let toWriteTextObjects = [];
|
|
834
|
-
/** Reports whether a layout node contains
|
|
871
|
+
/** Reports whether a layout node contains text, not only line breaks. */
|
|
835
872
|
const hasRenderableContent = (textObject) =>
|
|
836
873
|
(textObject.value !== undefined &&
|
|
837
874
|
textObject.value !== null &&
|
|
838
|
-
textObject.value !== ""
|
|
839
|
-
textObject.value !== "[@@DONOT_RENDER_THIS@@]") ||
|
|
875
|
+
textObject.value !== "") ||
|
|
840
876
|
textObject.childs?.some(hasRenderableContent);
|
|
841
877
|
|
|
878
|
+
/** Adds laid-out objects, recording the first line height. */
|
|
879
|
+
const addLaidOutObjects = (newToWriteObjects, paragraphHeight) => {
|
|
880
|
+
toWriteTextObjects = [...toWriteTextObjects, ...newToWriteObjects];
|
|
881
|
+
|
|
882
|
+
if (!firstLineHeight) {
|
|
883
|
+
this._lineHeight = firstLineHeight = toWriteTextObjects[0].lineHeight;
|
|
884
|
+
if (!this._firstLineHeight) {
|
|
885
|
+
this._firstLineHeight = this._lineHeight; // used in textbox coordinate computation
|
|
886
|
+
}
|
|
887
|
+
}
|
|
888
|
+
totalHeight += paragraphHeight;
|
|
889
|
+
};
|
|
890
|
+
|
|
842
891
|
const writeValue = (textObject) => {
|
|
843
892
|
textObject.lineID = textObject.lineID || Date.now() * Math.random();
|
|
844
|
-
textObject.lineID =
|
|
845
|
-
|
|
846
|
-
|
|
893
|
+
textObject.lineID =
|
|
894
|
+
textObject.needsLineBreaker || textObject.lineBreak
|
|
895
|
+
? Date.now() * Math.random()
|
|
896
|
+
: textObject.lineID;
|
|
897
|
+
if (textObject.lineBreak) {
|
|
898
|
+
// A line break lays out as one empty line sized like its text; the
|
|
899
|
+
// normalization below turns it into line state, never into text.
|
|
900
|
+
const { toWriteTextObjects: newToWriteObjects, paragraphHeight } =
|
|
901
|
+
makeTextObjects(
|
|
902
|
+
this,
|
|
903
|
+
Object.assign({}, textObject, { value: "" }),
|
|
904
|
+
pathOptions,
|
|
905
|
+
textBox,
|
|
906
|
+
);
|
|
907
|
+
const lineBreak = newToWriteObjects[newToWriteObjects.length - 1];
|
|
908
|
+
lineBreak.lineBreak = true;
|
|
909
|
+
// Flowed text marks the last word of the previous line, so an empty
|
|
910
|
+
// break line still carries one empty word.
|
|
911
|
+
if (!lineBreak.wordsInLine.length) {
|
|
912
|
+
lineBreak.wordsInLine.push(new Word("", pathOptions));
|
|
913
|
+
}
|
|
914
|
+
addLaidOutObjects(newToWriteObjects, paragraphHeight);
|
|
915
|
+
return;
|
|
916
|
+
}
|
|
847
917
|
// Want to allow empty string to pass through. Undefined and null elements, stay out!
|
|
848
918
|
if (textObject.value !== undefined && textObject.value !== null) {
|
|
849
919
|
textObject.styles.color = textObject.styles.color
|
|
@@ -851,16 +921,7 @@ exports._layoutText = function _layoutText(textObjects, textBox, pathOptions) {
|
|
|
851
921
|
: pathOptions.color;
|
|
852
922
|
const { toWriteTextObjects: newToWriteObjects, paragraphHeight } =
|
|
853
923
|
makeTextObjects(this, textObject, pathOptions, textBox);
|
|
854
|
-
|
|
855
|
-
toWriteTextObjects = [...toWriteTextObjects, ...newToWriteObjects];
|
|
856
|
-
|
|
857
|
-
if (!firstLineHeight) {
|
|
858
|
-
this._lineHeight = firstLineHeight = toWriteTextObjects[0].lineHeight;
|
|
859
|
-
if (!this._firstLineHeight) {
|
|
860
|
-
this._firstLineHeight = this._lineHeight; // used in textbox coordinate computation
|
|
861
|
-
}
|
|
862
|
-
}
|
|
863
|
-
totalHeight += paragraphHeight;
|
|
924
|
+
addLaidOutObjects(newToWriteObjects, paragraphHeight);
|
|
864
925
|
}
|
|
865
926
|
if (textObject.tag && textObject.childs.length) {
|
|
866
927
|
// console.log(textObject);
|
|
@@ -931,14 +992,18 @@ exports._layoutText = function _layoutText(textObjects, textBox, pathOptions) {
|
|
|
931
992
|
});
|
|
932
993
|
}
|
|
933
994
|
};
|
|
995
|
+
// Top-level nodes share a line, as children of a block element do, so
|
|
996
|
+
// inline runs outside any element are not split onto separate lines.
|
|
997
|
+
const topLevelLineID = Date.now() * Math.random();
|
|
934
998
|
textObjects.forEach((textObject) => {
|
|
999
|
+
textObject.lineID = textObject.lineID || topLevelLineID;
|
|
935
1000
|
writeValue(textObject);
|
|
936
1001
|
});
|
|
937
1002
|
|
|
938
1003
|
const normalizedTextObjects = [];
|
|
939
1004
|
let pendingBreaks = [];
|
|
940
1005
|
const replacementLineIDs = new Map();
|
|
941
|
-
/** Converts pending
|
|
1006
|
+
/** Converts pending line breaks into line state: blank lines and line IDs. */
|
|
942
1007
|
const appendPendingBreaks = (nextTextObject) => {
|
|
943
1008
|
const previous = normalizedTextObjects[normalizedTextObjects.length - 1];
|
|
944
1009
|
if (previous) previous.lineComplete = true;
|
|
@@ -963,12 +1028,13 @@ exports._layoutText = function _layoutText(textObjects, textBox, pathOptions) {
|
|
|
963
1028
|
pendingBreaks = [];
|
|
964
1029
|
};
|
|
965
1030
|
toWriteTextObjects.forEach((textObject, index, objects) => {
|
|
966
|
-
|
|
967
|
-
const
|
|
1031
|
+
// Whitespace that only precedes a line break would start a blank line.
|
|
1032
|
+
const beforeLineBreak =
|
|
1033
|
+
!textObject.lineBreak &&
|
|
968
1034
|
textObject.text.trim() == "" &&
|
|
969
|
-
objects[index + 1]?.
|
|
970
|
-
if (
|
|
971
|
-
if (
|
|
1035
|
+
objects[index + 1]?.lineBreak;
|
|
1036
|
+
if (beforeLineBreak) return;
|
|
1037
|
+
if (textObject.lineBreak) {
|
|
972
1038
|
pendingBreaks.push(textObject);
|
|
973
1039
|
return;
|
|
974
1040
|
}
|
|
@@ -1297,7 +1363,8 @@ function makeTextObjects(self, textObject = {}, pathOptions, textBox = {}) {
|
|
|
1297
1363
|
opacity: parseFloat(textObject.styles.opacity || pathOptions.opacity || 1),
|
|
1298
1364
|
underline: textObject.underline || pathOptions.underline,
|
|
1299
1365
|
strikeOut: textObject.strikeOut || pathOptions.strikeOut,
|
|
1300
|
-
size
|
|
1366
|
+
// Text outside any HTML element has no inherited size of its own.
|
|
1367
|
+
size: size,
|
|
1301
1368
|
alignHorizontal: alignHorizontal,
|
|
1302
1369
|
alignVertical: alignVertical,
|
|
1303
1370
|
font: self._getFont(textObject),
|
|
@@ -1339,7 +1406,7 @@ function makeTextObjects(self, textObject = {}, pathOptions, textBox = {}) {
|
|
|
1339
1406
|
const previousLine = toWriteTextObjects[end];
|
|
1340
1407
|
let lineComplete, fini;
|
|
1341
1408
|
|
|
1342
|
-
if (text === "" && !self._flow) {
|
|
1409
|
+
if (text === "" && !self._flow && !textObject.lineBreak) {
|
|
1343
1410
|
// turning off flow with empty text so
|
|
1344
1411
|
previousLine.lastLine = true; // need to make previous line, the last.
|
|
1345
1412
|
}
|
package/lib/recipe/utils.js
CHANGED
|
@@ -1,5 +1,28 @@
|
|
|
1
1
|
const ANNOTATION_PREFIX = "Annots";
|
|
2
2
|
|
|
3
|
+
/** Recipe page content-stream lifecycle states. */
|
|
4
|
+
const PAGE_CONTEXT_STATE = Object.freeze({
|
|
5
|
+
IDLE: "idle",
|
|
6
|
+
ACTIVE_NEW: "active-new",
|
|
7
|
+
ACTIVE_EDIT: "active-edit",
|
|
8
|
+
PAUSED_NEW: "paused-new",
|
|
9
|
+
PAUSED_EDIT: "paused-edit",
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Copies nested option objects and arrays without discarding callback values.
|
|
14
|
+
* @private
|
|
15
|
+
*/
|
|
16
|
+
function cloneOptions(value) {
|
|
17
|
+
if (Array.isArray(value)) return value.map(cloneOptions);
|
|
18
|
+
if (value && typeof value === "object") {
|
|
19
|
+
return Object.fromEntries(
|
|
20
|
+
Object.entries(value).map(([key, item]) => [key, cloneOptions(item)]),
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
return value;
|
|
24
|
+
}
|
|
25
|
+
|
|
3
26
|
/**
|
|
4
27
|
* Append PDF Page with annotations.
|
|
5
28
|
*
|
|
@@ -72,7 +95,36 @@ function appendPDFPagesFromPDFWithAnnotations(
|
|
|
72
95
|
}
|
|
73
96
|
}
|
|
74
97
|
|
|
98
|
+
/**
|
|
99
|
+
* Resolves a text font size from `size`, its `fontSize` alias, or a fallback,
|
|
100
|
+
* rejecting a size that is not greater than zero. Zero and negative sizes have
|
|
101
|
+
* no usable meaning: they draw nothing readable and measure to nonsensical font
|
|
102
|
+
* metrics, so they are reported as invalid input naming the option and the
|
|
103
|
+
* value. Omitting both options, or passing `null` or `undefined`, selects the
|
|
104
|
+
* fallback.
|
|
105
|
+
* @param {Object} [options] - Options holding `size` or `fontSize`.
|
|
106
|
+
* @param {number} [fallback] - Size used when neither option is given.
|
|
107
|
+
* @returns {number} The resolved font size in PDF points.
|
|
108
|
+
* @throws {RangeError} If the given size is not greater than zero.
|
|
109
|
+
*/
|
|
110
|
+
function resolveFontSize(options = {}, fallback) {
|
|
111
|
+
const name = options.size == null ? "fontSize" : "size";
|
|
112
|
+
const size = options[name];
|
|
113
|
+
if (size == null) {
|
|
114
|
+
return fallback;
|
|
115
|
+
}
|
|
116
|
+
if (!(size > 0)) {
|
|
117
|
+
throw new RangeError(
|
|
118
|
+
`Text ${name} must be a number greater than zero, received ${size}`,
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
return size;
|
|
122
|
+
}
|
|
123
|
+
|
|
75
124
|
exports.ANNOTATION_PREFIX = ANNOTATION_PREFIX;
|
|
125
|
+
exports.PAGE_CONTEXT_STATE = PAGE_CONTEXT_STATE;
|
|
126
|
+
exports.cloneOptions = cloneOptions;
|
|
127
|
+
exports.resolveFontSize = resolveFontSize;
|
|
76
128
|
exports.appendPDFPageFromPDFWithAnnotations =
|
|
77
129
|
appendPDFPageFromPDFWithAnnotations;
|
|
78
130
|
exports.appendPDFPagesFromPDFWithAnnotations =
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const { xObjectForm } = require("./xObjectForm");
|
|
2
|
+
const { resolveFontSize } = require("./utils");
|
|
2
3
|
|
|
3
4
|
exports._getPathOptions = function _getPathOptions(
|
|
4
5
|
options = {},
|
|
@@ -13,7 +14,7 @@ exports._getPathOptions = function _getPathOptions(
|
|
|
13
14
|
originX,
|
|
14
15
|
originY,
|
|
15
16
|
font: this._getFont(options),
|
|
16
|
-
size: options
|
|
17
|
+
size: resolveFontSize(options, this.current.defaultFontSize),
|
|
17
18
|
charSpace: options.charSpace || 0,
|
|
18
19
|
underline: false,
|
|
19
20
|
strikeOut: false,
|
|
@@ -54,12 +55,6 @@ exports._getPathOptions = function _getPathOptions(
|
|
|
54
55
|
pathOptions.strokeGsId = extGStates.stroke;
|
|
55
56
|
pathOptions.fillGsId = extGStates.fill;
|
|
56
57
|
|
|
57
|
-
if (options.size || options.fontSize) {
|
|
58
|
-
const size = options.size || options.fontSize;
|
|
59
|
-
if (!isNaN(size)) {
|
|
60
|
-
pathOptions.size = size <= 0 ? 1 : size;
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
58
|
if (options.width || options.lineWidth) {
|
|
64
59
|
const width = options.width || options.lineWidth;
|
|
65
60
|
if (!isNaN(width)) {
|
package/lib/recipe/vector.js
CHANGED
|
@@ -668,7 +668,6 @@ exports.opacity = function opacity(value) {
|
|
|
668
668
|
* @name fill
|
|
669
669
|
* @function
|
|
670
670
|
* @memberof Recipe#
|
|
671
|
-
* @param {string|number[]} [color] - The requested fill color.
|
|
672
671
|
* @returns {Recipe} The recipe instance.
|
|
673
672
|
*/
|
|
674
673
|
exports.fill = function fill() {
|
|
@@ -682,7 +681,6 @@ exports.fill = function fill() {
|
|
|
682
681
|
* @name stroke
|
|
683
682
|
* @function
|
|
684
683
|
* @memberof Recipe#
|
|
685
|
-
* @param {string|number[]} [color] - The requested stroke color.
|
|
686
684
|
* @returns {Recipe} The recipe instance.
|
|
687
685
|
*/
|
|
688
686
|
exports.stroke = function stroke() {
|
package/muhammara.d.ts
CHANGED
|
@@ -109,6 +109,7 @@ declare namespace muhammara {
|
|
|
109
109
|
}
|
|
110
110
|
|
|
111
111
|
export interface GraphicOptions extends ColorOptions {
|
|
112
|
+
/** "clip" intersects the clipping region without painting; scope with q()/Q(). */
|
|
112
113
|
type?: "stroke" | "fill" | "clip";
|
|
113
114
|
width?: number;
|
|
114
115
|
close?: boolean;
|
|
@@ -215,11 +216,15 @@ declare namespace muhammara {
|
|
|
215
216
|
): this;
|
|
216
217
|
TJ(value: string | Glyph, options?: TextRenderOptions): this;
|
|
217
218
|
writeFreeCode(freeCode: string): this;
|
|
219
|
+
/** Require at least two complete finite coordinate pairs; invalid input emits no operators. */
|
|
218
220
|
drawPath(...parameters: any[]): this; // This can't be materialized in TypeScript
|
|
219
221
|
////drawPath(...xyPairs: number[], options: GraphicOptions): this;
|
|
220
222
|
drawPath(xyPairs: Array<[number, number]>, options: GraphicOptions): this;
|
|
223
|
+
/** Coordinates, radius, and calculated circle geometry must remain finite. */
|
|
221
224
|
drawCircle(x: PosX, y: PosY, r: number, options?: GraphicOptions): this;
|
|
225
|
+
/** Coordinates and edge length must be finite. */
|
|
222
226
|
drawSquare(x: PosX, y: PosY, l: number, options?: GraphicOptions): this;
|
|
227
|
+
/** Coordinates and dimensions must be finite. */
|
|
223
228
|
drawRectangle(
|
|
224
229
|
x: PosX,
|
|
225
230
|
y: PosY,
|
|
@@ -227,6 +232,7 @@ declare namespace muhammara {
|
|
|
227
232
|
h: number,
|
|
228
233
|
options?: GraphicOptions,
|
|
229
234
|
): this;
|
|
235
|
+
/** Coordinates, font size, and calculated underline geometry must remain finite. */
|
|
230
236
|
writeText(text: string, x: PosX, y: PosY, options?: WriteTextOptions): this;
|
|
231
237
|
drawImage(
|
|
232
238
|
x: PosX,
|
|
@@ -287,7 +293,8 @@ declare namespace muhammara {
|
|
|
287
293
|
export interface PDFWriterToContinueOptions {
|
|
288
294
|
modifiedFilePath?: string;
|
|
289
295
|
modifiedStream?: PDFRStreamForFile; // TODO
|
|
290
|
-
|
|
296
|
+
/** Log file path or synchronous byte writer returning the number of bytes written. */
|
|
297
|
+
log?: string | ByteWriter;
|
|
291
298
|
}
|
|
292
299
|
|
|
293
300
|
export interface PDFRecryptOptions extends PDFWriterOptions {
|
|
@@ -863,6 +870,7 @@ declare namespace muhammara {
|
|
|
863
870
|
source: FilePath | ReadStream,
|
|
864
871
|
options?: AppendOptions,
|
|
865
872
|
): number[];
|
|
873
|
+
/** Calls the optional callback with no arguments and globalThis as its receiver. */
|
|
866
874
|
mergePDFPagesToPage(
|
|
867
875
|
page: PDFPage,
|
|
868
876
|
file: FilePath | PDFRStreamForFile,
|
|
@@ -1226,6 +1234,7 @@ declare namespace muhammara {
|
|
|
1226
1234
|
/** Make the rendered text open this URL. */
|
|
1227
1235
|
link?: string;
|
|
1228
1236
|
charSpace?: number;
|
|
1237
|
+
/** Text fill color: `#gg`, `#rrggbb`, `#ccmmyykk`, `%r,g,b` percentages, a 0-255 component array, or a name registered with `chroma()`. Missing or unknown colors use `#1777d1`. */
|
|
1229
1238
|
color?: Color;
|
|
1230
1239
|
colorspace?: Colorspace;
|
|
1231
1240
|
flow?: boolean;
|
|
@@ -1235,9 +1244,9 @@ declare namespace muhammara {
|
|
|
1235
1244
|
rotation?: number;
|
|
1236
1245
|
rotationOrigin?: readonly [number, number];
|
|
1237
1246
|
font?: string;
|
|
1238
|
-
/** Font size in points for text() and textDimensions(); defaults to 14 when both fontSize and size are omitted. */
|
|
1247
|
+
/** Font size in points for text() and textDimensions(); defaults to 14 when both fontSize and size are omitted. A size that is not greater than zero throws RangeError. */
|
|
1239
1248
|
fontSize?: number;
|
|
1240
|
-
/** Font size in points for text() and textDimensions(); defaults to 14. */
|
|
1249
|
+
/** Font size in points for text() and textDimensions(); defaults to 14. A size that is not greater than zero throws RangeError. */
|
|
1241
1250
|
size?: number;
|
|
1242
1251
|
bold?: boolean;
|
|
1243
1252
|
italic?: boolean;
|
|
@@ -1301,14 +1310,20 @@ declare namespace muhammara {
|
|
|
1301
1310
|
name: Field;
|
|
1302
1311
|
text?: string;
|
|
1303
1312
|
width?: number;
|
|
1313
|
+
/** Cell text-box options, including onClip callbacks preserved during table layout. */
|
|
1304
1314
|
cell?: TextBox;
|
|
1315
|
+
/** Header text styles, independent of body styles; booleans use the default header style. Table-level header options take precedence. */
|
|
1305
1316
|
header?: boolean | TextOptions;
|
|
1317
|
+
/** Final header text-box overrides, applied after header styles and alignToData. */
|
|
1306
1318
|
hcell?: TextBox;
|
|
1307
1319
|
renderer?: (
|
|
1308
1320
|
this: void,
|
|
1321
|
+
/** The own cell value; missing, inherited, and nullish values arrive as `""`. */
|
|
1309
1322
|
text: undefined extends TableFieldValue<RecordType, Field>
|
|
1310
|
-
? Exclude<TableFieldValue<RecordType, Field>, undefined> | ""
|
|
1311
|
-
: TableFieldValue<RecordType, Field
|
|
1323
|
+
? Exclude<TableFieldValue<RecordType, Field>, null | undefined> | ""
|
|
1324
|
+
: null extends TableFieldValue<RecordType, Field>
|
|
1325
|
+
? Exclude<TableFieldValue<RecordType, Field>, null | undefined> | ""
|
|
1326
|
+
: TableFieldValue<RecordType, Field>,
|
|
1312
1327
|
record: RecordType,
|
|
1313
1328
|
field: Field,
|
|
1314
1329
|
row: number,
|
|
@@ -1335,16 +1350,20 @@ declare namespace muhammara {
|
|
|
1335
1350
|
interface TableOptions<
|
|
1336
1351
|
RecordType extends object = Record<string, unknown>,
|
|
1337
1352
|
> extends Omit<TextOptions, "overflow"> {
|
|
1353
|
+
/** Per-segment height, also bounded by the current page's bottom margin. */
|
|
1338
1354
|
height?: number;
|
|
1355
|
+
/** Comma-separated names are trimmed; array entries preserve exact keys. */
|
|
1339
1356
|
order?:
|
|
1340
1357
|
| string
|
|
1341
1358
|
| TableField<RecordType>[]
|
|
1342
1359
|
| readonly [TableField<RecordType>, ...TableField<RecordType>[]];
|
|
1343
1360
|
columns?: readonly TableColumnOptions<RecordType>[];
|
|
1361
|
+
/** Enables headers and overrides column header styles; body text styles are not inherited. */
|
|
1344
1362
|
header?:
|
|
1345
1363
|
boolean | (TextOptions & { alignToData?: boolean; cell?: TextBox });
|
|
1346
1364
|
border?: boolean | PolygonOptions;
|
|
1347
1365
|
row?: TextOptions & { nth?: "even" | "odd"; cell?: TextBox };
|
|
1366
|
+
/** Called once per overflow. A continuing destination must fit the row and repeated header or table() throws RangeError; ending the page without starting another throws Error. */
|
|
1348
1367
|
overflow?: (
|
|
1349
1368
|
this: Recipe,
|
|
1350
1369
|
recipe: Recipe,
|
|
@@ -1363,6 +1382,8 @@ declare namespace muhammara {
|
|
|
1363
1382
|
attributes: Array<{ name: string; value: string | null }>;
|
|
1364
1383
|
styles: Record<string, string | number | number[]>;
|
|
1365
1384
|
needsLineBreaker: boolean;
|
|
1385
|
+
/** True for a `<br>` element, which ends the current line. */
|
|
1386
|
+
lineBreak: boolean;
|
|
1366
1387
|
size: number | undefined;
|
|
1367
1388
|
sizeRatio: number;
|
|
1368
1389
|
sizeRatios: number[];
|
|
@@ -1697,9 +1718,9 @@ declare namespace muhammara {
|
|
|
1697
1718
|
lineWidth(width: number): Recipe;
|
|
1698
1719
|
/** Set fill and stroke opacity from 0 (transparent) to 1 (opaque). */
|
|
1699
1720
|
opacity(opacity: number): Recipe;
|
|
1700
|
-
fill(
|
|
1701
|
-
stroke(
|
|
1702
|
-
fillAndStroke(
|
|
1721
|
+
fill(): Recipe;
|
|
1722
|
+
stroke(): Recipe;
|
|
1723
|
+
fillAndStroke(): Recipe;
|
|
1703
1724
|
n_gon(
|
|
1704
1725
|
cx: number,
|
|
1705
1726
|
cy: number,
|
package/package.json
CHANGED