@breakside/jskit 2022.23.0 → 2022.24.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/Frameworks/DOM.jsframework/Info.json +2 -2
- package/Frameworks/DOM.jsframework/io.breakside.JSKit.DOM-bundle.js +2 -2
- package/Frameworks/FontKit.jsframework/Info.json +2 -2
- package/Frameworks/FontKit.jsframework/io.breakside.JSKit.FontKit-bundle.js +2 -2
- package/Frameworks/Foundation.jsframework/Info.json +2 -2
- package/Frameworks/Foundation.jsframework/JS/JSAttributedString.js +139 -10
- package/Frameworks/Foundation.jsframework/JS/JSParagraphStyle.js +71 -0
- package/Frameworks/Foundation.jsframework/JS/JSTextAttachment.js +1 -1
- package/Frameworks/Foundation.jsframework/JS/JSTextContainer.js +56 -29
- package/Frameworks/Foundation.jsframework/JS/JSTextFrame.js +2 -32
- package/Frameworks/Foundation.jsframework/JS/JSTextFramesetter.js +125 -22
- package/Frameworks/Foundation.jsframework/JS/JSTextLayoutManager.js +11 -3
- package/Frameworks/Foundation.jsframework/JS/JSTextLine.js +4 -3
- package/Frameworks/Foundation.jsframework/JS/JSTextRun.js +5 -3
- package/Frameworks/Foundation.jsframework/JS/JSTextTypesetter.js +41 -25
- package/Frameworks/Foundation.jsframework/JS/String+JS.js +35 -0
- package/Frameworks/Foundation.jsframework/JS/UnicodeChar.js +8 -0
- package/Frameworks/Foundation.jsframework/io.breakside.JSKit.Foundation-bundle.js +2 -2
- package/Frameworks/Foundation.jsframework/sources.json +3 -0
- package/Frameworks/SecurityKit.jsframework/Info.json +2 -2
- package/Frameworks/SecurityKit.jsframework/io.breakside.JSKit.SecurityKit-bundle.js +2 -2
- package/Info.json +2 -2
- package/Node/io.breakside.jskit-bundle.js +2 -2
- package/Node/jskit +1 -0
- package/Root/Frameworks/APIKit/Info.yaml +1 -1
- package/Root/Frameworks/APIKitTesting/Info.yaml +1 -1
- package/Root/Frameworks/AuthKit/Info.yaml +1 -1
- package/Root/Frameworks/CSSOM/Info.yaml +1 -1
- package/Root/Frameworks/ChartKit/CHAxis.js +5 -5
- package/Root/Frameworks/ChartKit/CHPieChart.js +1 -1
- package/Root/Frameworks/ChartKit/Info.yaml +1 -1
- package/Root/Frameworks/ConferenceKit/Info.yaml +1 -1
- package/Root/Frameworks/DBKit/Info.yaml +1 -1
- package/Root/Frameworks/DOM/Info.yaml +1 -1
- package/Root/Frameworks/Dispatch/Info.yaml +1 -1
- package/Root/Frameworks/FontKit/Info.yaml +1 -1
- package/Root/Frameworks/Foundation/Info.yaml +1 -1
- package/Root/Frameworks/Foundation/JSAttributedString.js +139 -10
- package/Root/Frameworks/Foundation/JSParagraphStyle.js +71 -0
- package/Root/Frameworks/Foundation/JSTextAttachment.js +1 -1
- package/Root/Frameworks/Foundation/JSTextContainer.js +56 -29
- package/Root/Frameworks/Foundation/JSTextFrame.js +2 -32
- package/Root/Frameworks/Foundation/JSTextFramesetter.js +125 -22
- package/Root/Frameworks/Foundation/JSTextLayoutManager.js +11 -3
- package/Root/Frameworks/Foundation/JSTextLine.js +4 -3
- package/Root/Frameworks/Foundation/JSTextRun.js +5 -3
- package/Root/Frameworks/Foundation/JSTextTypesetter.js +41 -25
- package/Root/Frameworks/Foundation/String+JS.js +35 -0
- package/Root/Frameworks/Foundation/UnicodeChar.js +8 -0
- package/Root/Frameworks/ImageKit/Info.yaml +1 -1
- package/Root/Frameworks/MediaKit/Info.yaml +1 -1
- package/Root/Frameworks/MediaKitUI/Info.yaml +1 -1
- package/Root/Frameworks/NotificationKit/Info.yaml +1 -1
- package/Root/Frameworks/PDFKit/Info.yaml +1 -1
- package/Root/Frameworks/QRKit/Info.yaml +1 -1
- package/Root/Frameworks/SearchKit/Info.yaml +1 -1
- package/Root/Frameworks/SecurityKit/Info.yaml +1 -1
- package/Root/Frameworks/ServerKit/Info.yaml +1 -1
- package/Root/Frameworks/ServerKitTesting/Info.yaml +1 -1
- package/Root/Frameworks/TestKit/Info.yaml +1 -1
- package/Root/Frameworks/UIKit/Info.yaml +1 -1
- package/Root/Frameworks/UIKit/UIHTMLContentEditableTextInputManager.js +36 -0
- package/Root/Frameworks/UIKit/UIHTMLDisplayServer.js +1 -1
- package/Root/Frameworks/UIKit/UIHTMLTextField.js +1 -1
- package/Root/Frameworks/UIKit/UIHTMLTextFrame.js +19 -91
- package/Root/Frameworks/UIKit/UIHTMLTextFramesetter.js +52 -9
- package/Root/Frameworks/UIKit/UIHTMLTextLine.js +95 -30
- package/Root/Frameworks/UIKit/UIHTMLTextRun.js +22 -9
- package/Root/Frameworks/UIKit/UIHTMLTextTypesetter.js +334 -459
- package/Root/Frameworks/UIKit/UIHTMLWindowServer.js +16 -0
- package/Root/Frameworks/UIKit/UIScrollView.js +12 -0
- package/Root/Frameworks/UIKit/UISegmentedControl.js +6 -6
- package/Root/Frameworks/UIKit/UITextAttachmentView.js +1 -1
- package/Root/Frameworks/UIKit/UITextEditor.js +210 -25
- package/Root/Frameworks/UIKit/UITextField.js +58 -31
- package/Root/Frameworks/UIKit/UITextInput.js +3 -1
- package/Root/Frameworks/UIKit/UITextInputManager.js +2 -2
- package/Root/Frameworks/UIKit/UITextLayer.js +39 -26
- package/Root/Frameworks/UIKit/UITokenField.js +4 -4
- package/Root/Frameworks/UIKit/UIView.js +3 -1
- package/Root/Frameworks/UIKit/UIWindow.js +16 -3
- package/Root/Frameworks/UIKitTesting/Info.yaml +1 -1
- package/package.json +1 -1
|
@@ -18,15 +18,18 @@
|
|
|
18
18
|
// #import "JSTextTypesetter.js"
|
|
19
19
|
// #import "JSTextFrame.js"
|
|
20
20
|
// #import "JSAttributedString.js"
|
|
21
|
+
// #import "JSParagraphStyle.js"
|
|
21
22
|
'use strict';
|
|
22
23
|
|
|
23
24
|
(function(){
|
|
24
25
|
|
|
26
|
+
var logger = JSLog("foundation", "text");
|
|
27
|
+
|
|
25
28
|
JSClass("JSTextFramesetter", JSObject, {
|
|
26
29
|
|
|
27
30
|
typesetter: JSReadOnlyProperty('_typesetter', null),
|
|
28
31
|
attributedString: JSDynamicProperty(),
|
|
29
|
-
|
|
32
|
+
defaultParagraphStyle: JSDynamicProperty("_defaultParagraphStyle", null),
|
|
30
33
|
|
|
31
34
|
init: function(){
|
|
32
35
|
this.initWithTypesetter(JSTextTypesetter.init());
|
|
@@ -34,7 +37,7 @@ JSClass("JSTextFramesetter", JSObject, {
|
|
|
34
37
|
|
|
35
38
|
initWithTypesetter: function(typesetter){
|
|
36
39
|
this._typesetter = typesetter;
|
|
37
|
-
this.
|
|
40
|
+
this._defaultParagraphStyle = JSParagraphStyle.init();
|
|
38
41
|
},
|
|
39
42
|
|
|
40
43
|
getAttributedString: function(){
|
|
@@ -45,8 +48,8 @@ JSClass("JSTextFramesetter", JSObject, {
|
|
|
45
48
|
this._typesetter.attributedString = attributedString;
|
|
46
49
|
},
|
|
47
50
|
|
|
48
|
-
constructFrame: function(lines, size
|
|
49
|
-
return JSTextFrame.initWithLines(lines, size
|
|
51
|
+
constructFrame: function(lines, size){
|
|
52
|
+
return JSTextFrame.initWithLines(lines, size);
|
|
50
53
|
},
|
|
51
54
|
|
|
52
55
|
createFrame: function(size, range, maximumLines){
|
|
@@ -61,35 +64,96 @@ JSClass("JSTextFramesetter", JSObject, {
|
|
|
61
64
|
var widthLimit = size.width || Number.MAX_VALUE;
|
|
62
65
|
var heightLimit = size.height || Number.MAX_VALUE;
|
|
63
66
|
var lineLimit = maximumLines || Number.MAX_VALUE;
|
|
64
|
-
var
|
|
67
|
+
var lineWidthLimit;
|
|
68
|
+
var attributes = this._typesetter.resolveAttributes(this._typesetter.attributedString.attributesAtIndex(remianingRange.location));
|
|
69
|
+
var paragraphStyle = this._defaultParagraphStyle.styleWithAttributes(attributes);
|
|
70
|
+
var characterIterator = this._typesetter.attributedString.string.userPerceivedCharacterIterator(remianingRange.location);
|
|
71
|
+
characterIterator.decrement();
|
|
65
72
|
do{
|
|
66
|
-
|
|
73
|
+
lineWidthLimit = widthLimit;
|
|
74
|
+
if (lineWidthLimit < Number.MAX_VALUE){
|
|
75
|
+
if (characterIterator.isParagraphBreak){
|
|
76
|
+
lineWidthLimit -= paragraphStyle.firstLineHeadIndent;
|
|
77
|
+
}else{
|
|
78
|
+
lineWidthLimit -= paragraphStyle.headIndent;
|
|
79
|
+
}
|
|
80
|
+
lineWidthLimit -= paragraphStyle.tailIndent;
|
|
81
|
+
}
|
|
82
|
+
lineRange = this._typesetter.suggestLineBreak(lineWidthLimit, remianingRange, this.effectiveLineBreakMode(paragraphStyle.lineBreakMode, lines.length + 1, lineLimit));
|
|
67
83
|
line = this._typesetter.createLine(lineRange);
|
|
68
84
|
line.origin.y = y;
|
|
85
|
+
if (paragraphStyle.lineHeightMultiple > 0){
|
|
86
|
+
line.size.height *= paragraphStyle.lineHeightMultiple;
|
|
87
|
+
}
|
|
88
|
+
if (line.size.height < paragraphStyle.minimumLineHeight){
|
|
89
|
+
line.size.height = paragraphStyle.minimumLineHeight;
|
|
90
|
+
}
|
|
69
91
|
y += line.size.height;
|
|
70
92
|
if (y <= heightLimit){
|
|
71
|
-
spacing = Math.min(heightLimit - y, (this.attributes.lineSpacing - 1.0) * line.size.height);
|
|
72
|
-
line.size.height += Math.max(0, spacing);
|
|
73
|
-
y += spacing;
|
|
74
|
-
lines.push(line);
|
|
75
93
|
remianingRange.advance(line.range.length);
|
|
76
|
-
|
|
77
|
-
|
|
94
|
+
characterIterator = this._typesetter.attributedString.string.userPerceivedCharacterIterator(remianingRange.location);
|
|
95
|
+
characterIterator.decrement();
|
|
96
|
+
if (characterIterator.isParagraphBreak){
|
|
97
|
+
y += Math.max(0, Math.min(heightLimit - y, paragraphStyle.paragraphSpacing));
|
|
98
|
+
attributes = this._typesetter.resolveAttributes(this._typesetter.attributedString.attributesAtIndex(remianingRange.location));
|
|
99
|
+
paragraphStyle = this._defaultParagraphStyle.styleWithAttributes(attributes);
|
|
100
|
+
y += Math.max(0, Math.min(heightLimit - y, paragraphStyle.beforeParagraphSpacing));
|
|
101
|
+
}
|
|
102
|
+
y += Math.max(0, Math.min(heightLimit - y, paragraphStyle.lineSpacing));
|
|
103
|
+
lines.push(line);
|
|
78
104
|
}
|
|
79
105
|
} while (lineRange.length > 0 && remianingRange.length > 0 && y < heightLimit && lines.length < lineLimit);
|
|
80
106
|
|
|
81
|
-
if (
|
|
107
|
+
if (paragraphStyle.lineBreakMode == JSLineBreakMode.truncateTail && lines.length > 0){
|
|
82
108
|
line = lines.pop();
|
|
83
109
|
if (remianingRange.length > 0 && lineLimit > lines.length + 1){
|
|
84
110
|
// we got truncated because of height. Re-run the last line so it
|
|
85
111
|
// gets broken according to truncation rules rather than word break
|
|
86
|
-
lineRange = this._typesetter.suggestLineBreak(widthLimit, JSRange(line.range.location, line.range.length + remianingRange.length),
|
|
112
|
+
lineRange = this._typesetter.suggestLineBreak(widthLimit, JSRange(line.range.location, line.range.length + remianingRange.length), paragraphStyle.lineBreakMode);
|
|
87
113
|
line = this._typesetter.createLine(lineRange);
|
|
88
114
|
}
|
|
89
115
|
var truncated = line.truncatedLine(widthLimit);
|
|
90
116
|
lines.push(truncated);
|
|
91
117
|
}
|
|
92
|
-
|
|
118
|
+
var frame = this.constructFrame(lines, size);
|
|
119
|
+
this._alignLinesInFrame(frame);
|
|
120
|
+
return frame;
|
|
121
|
+
},
|
|
122
|
+
|
|
123
|
+
_resizeFrame: function(frame, size){
|
|
124
|
+
if (size.width < frame._usedSize.width || size.height < frame._usedSize.height){
|
|
125
|
+
throw new Error("Cannot adjust text frame to smaller than its used size");
|
|
126
|
+
}
|
|
127
|
+
frame._size = JSSize(size);
|
|
128
|
+
this._alignLinesInFrame(frame);
|
|
129
|
+
},
|
|
130
|
+
|
|
131
|
+
_alignLinesInFrame: function(frame){
|
|
132
|
+
var i, l;
|
|
133
|
+
var line;
|
|
134
|
+
var attributes;
|
|
135
|
+
var headIndent = 0;
|
|
136
|
+
var characterIterator;
|
|
137
|
+
var paragraphStyle;
|
|
138
|
+
for (i = 0, l = frame.lines.length; i < l; ++i){
|
|
139
|
+
line = frame.lines[i];
|
|
140
|
+
attributes = this._typesetter.resolveAttributes(this._typesetter.attributedString.attributesAtIndex(line.range.location));
|
|
141
|
+
paragraphStyle = this._defaultParagraphStyle.styleWithAttributes(attributes);
|
|
142
|
+
characterIterator = this._typesetter.attributedString.string.userPerceivedCharacterIterator(line.range.location);
|
|
143
|
+
characterIterator.decrement();
|
|
144
|
+
if (characterIterator.isParagraphBreak){
|
|
145
|
+
headIndent = paragraphStyle.firstLineHeadIndent;
|
|
146
|
+
}else{
|
|
147
|
+
headIndent = paragraphStyle.headIndent;
|
|
148
|
+
}
|
|
149
|
+
if (paragraphStyle.textAlignment === JSTextAlignment.left){
|
|
150
|
+
line.origin.x = headIndent;
|
|
151
|
+
}else if (paragraphStyle.textAlignment === JSTextAlignment.center){
|
|
152
|
+
line.origin.x = headIndent + (frame._size.width - headIndent - paragraphStyle.tailIndent - line.size.width + line.trailingWhitespaceWidth) / 2.0;
|
|
153
|
+
}else if (paragraphStyle.textAlignment === JSTextAlignment.right){
|
|
154
|
+
line.origin.x = (frame._size.width - line.size.width + line.trailingWhitespaceWidth) - paragraphStyle.tailIndent;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
93
157
|
},
|
|
94
158
|
|
|
95
159
|
effectiveLineBreakMode: function(frameLineBreakMode, lineNumber, lineLimit){
|
|
@@ -104,15 +168,54 @@ JSClass("JSTextFramesetter", JSObject, {
|
|
|
104
168
|
case JSLineBreakMode.characterWrap:
|
|
105
169
|
return frameLineBreakMode;
|
|
106
170
|
}
|
|
171
|
+
},
|
|
172
|
+
|
|
173
|
+
// Deprecated:
|
|
174
|
+
|
|
175
|
+
attributes: JSReadOnlyProperty("_attributes", null),
|
|
176
|
+
|
|
177
|
+
getAttributes: function(){
|
|
178
|
+
logger.warn("JSTextFramesetter.attributes is deprecated");
|
|
179
|
+
if (this._attributes === null){
|
|
180
|
+
var framesetter = this;
|
|
181
|
+
this._attributes = Object.create({}, {
|
|
182
|
+
lineBreakMode: {
|
|
183
|
+
get: function(){
|
|
184
|
+
return framesetter._defaultParagraphStyle.lineBreakMode;
|
|
185
|
+
},
|
|
186
|
+
set: function(lineBreakMode){
|
|
187
|
+
framesetter._defaultParagraphStyle.lineBreakMode = lineBreakMode;
|
|
188
|
+
}
|
|
189
|
+
},
|
|
190
|
+
textAlignment: {
|
|
191
|
+
get: function(){
|
|
192
|
+
return framesetter._defaultParagraphStyle.textAlignment;
|
|
193
|
+
},
|
|
194
|
+
set: function(textAlignment){
|
|
195
|
+
framesetter._defaultParagraphStyle.textAlignment = textAlignment;
|
|
196
|
+
}
|
|
197
|
+
},
|
|
198
|
+
lineSpacing: {
|
|
199
|
+
get: function(){
|
|
200
|
+
return framesetter._defaultParagraphStyle.lineHeightMultiple > 0 ? framesetter._defaultParagraphStyle.lineHeightMultiple : 1.0;
|
|
201
|
+
},
|
|
202
|
+
set: function(lineSpacing){
|
|
203
|
+
framesetter._defaultParagraphStyle.lineHeightMultiple = lineSpacing;
|
|
204
|
+
}
|
|
205
|
+
},
|
|
206
|
+
minimumLineHeight: {
|
|
207
|
+
get: function(){
|
|
208
|
+
return framesetter._defaultParagraphStyle.minimumLineHeight;
|
|
209
|
+
},
|
|
210
|
+
set: function(minimumLineHeight){
|
|
211
|
+
framesetter._defaultParagraphStyle.minimumLineHeight = minimumLineHeight;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
return this._attributes;
|
|
107
217
|
}
|
|
108
218
|
|
|
109
219
|
});
|
|
110
220
|
|
|
111
|
-
var JSTextParagraphAttributes = {
|
|
112
|
-
minimumLineHeight: 0,
|
|
113
|
-
lineSpacing: 1.0,
|
|
114
|
-
textAlignment: JSTextAlignment.left,
|
|
115
|
-
lineBreakMode: JSLineBreakMode.truncateTail
|
|
116
|
-
};
|
|
117
|
-
|
|
118
221
|
})();
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
// #import "CoreTypes.js"
|
|
18
18
|
// #import "JSAttributedString.js"
|
|
19
19
|
// #import "JSColor.js"
|
|
20
|
+
// #import "JSParagraphStyle.js"
|
|
20
21
|
'use strict';
|
|
21
22
|
|
|
22
23
|
(function(){
|
|
@@ -26,6 +27,7 @@ JSClass("JSTextLayoutManager", JSObject, {
|
|
|
26
27
|
textStorage: JSDynamicProperty('_textStorage', null),
|
|
27
28
|
defaultFont: JSDynamicProperty('_defaultFont', null),
|
|
28
29
|
defaultTextColor: JSDynamicProperty('_defaultTextColor', null),
|
|
30
|
+
defaultParagraphStyle: JSDynamicProperty("_defaultParagraphStyle", null),
|
|
29
31
|
includeEmptyFinalLine: false,
|
|
30
32
|
|
|
31
33
|
delegate: null,
|
|
@@ -37,6 +39,7 @@ JSClass("JSTextLayoutManager", JSObject, {
|
|
|
37
39
|
init: function(){
|
|
38
40
|
this._textContainers = [];
|
|
39
41
|
this._defaultTextColor = JSColor.black;
|
|
42
|
+
this._defaultParagraphStyle = JSParagraphStyle.init();
|
|
40
43
|
},
|
|
41
44
|
|
|
42
45
|
// MARK: - Managing Containers
|
|
@@ -106,6 +109,11 @@ JSClass("JSTextLayoutManager", JSObject, {
|
|
|
106
109
|
this.setNeedsLayout();
|
|
107
110
|
},
|
|
108
111
|
|
|
112
|
+
setDefaultParagraphStyle: function(paragraphStyle){
|
|
113
|
+
this._defaultParagraphStyle = paragraphStyle;
|
|
114
|
+
this.setNeedsLayout();
|
|
115
|
+
},
|
|
116
|
+
|
|
109
117
|
// MARK: - Private Helpers for finalizing runs
|
|
110
118
|
|
|
111
119
|
effectiveAttributedString: function(){
|
|
@@ -353,13 +361,13 @@ JSClass("JSTextLayoutManager", JSObject, {
|
|
|
353
361
|
rightX += 8;
|
|
354
362
|
}
|
|
355
363
|
}else{
|
|
356
|
-
characterRect = line.rectForCharacterAtIndex(remainingRange.end
|
|
364
|
+
characterRect = line.rectForCharacterAtIndex(remainingRange.end);
|
|
357
365
|
rightX = characterRect.origin.x;
|
|
358
366
|
}
|
|
359
367
|
if (remainingRange.location === line.range.location){
|
|
360
368
|
leftX = 0;
|
|
361
369
|
}else{
|
|
362
|
-
characterRect = line.rectForCharacterAtIndex(remainingRange.location
|
|
370
|
+
characterRect = line.rectForCharacterAtIndex(remainingRange.location);
|
|
363
371
|
leftX = characterRect.origin.x;
|
|
364
372
|
}
|
|
365
373
|
rect = JSRect(line.origin.x + leftX, line.origin.y, rightX - leftX, line.size.height);
|
|
@@ -406,7 +414,7 @@ var JSTextLayoutManagerLineEnumerator = function(layoutManager, index){
|
|
|
406
414
|
this._containerIndex = layoutManager._textContainerIndexForCharacterAtIndex(index);
|
|
407
415
|
var container = this.container;
|
|
408
416
|
if (container !== null){
|
|
409
|
-
this._lineIndex = container.lineIndexForCharacterAtIndex(index
|
|
417
|
+
this._lineIndex = container.lineIndexForCharacterAtIndex(index);
|
|
410
418
|
}else{
|
|
411
419
|
this._lineIndex = 0;
|
|
412
420
|
}
|
|
@@ -72,6 +72,7 @@ JSClass("JSTextLine", JSObject, {
|
|
|
72
72
|
}
|
|
73
73
|
var height = this._size.height;
|
|
74
74
|
for (i = 0, l = this._runs.length; i < l; ++i){
|
|
75
|
+
run = this._runs[i];
|
|
75
76
|
if (run.size.height - run.baseline + baseline > height){
|
|
76
77
|
height = run.size.height - run.baseline + baseline;
|
|
77
78
|
}
|
|
@@ -105,7 +106,7 @@ JSClass("JSTextLine", JSObject, {
|
|
|
105
106
|
rectForCharacterAtIndex: function(index){
|
|
106
107
|
var run = this.runForCharacterAtIndex(index);
|
|
107
108
|
if (run !== null){
|
|
108
|
-
var rect = run.rectForCharacterAtIndex(index
|
|
109
|
+
var rect = run.rectForCharacterAtIndex(index);
|
|
109
110
|
rect.origin.x += run.origin.x;
|
|
110
111
|
rect.origin.y += run.origin.y;
|
|
111
112
|
return rect;
|
|
@@ -132,7 +133,7 @@ JSClass("JSTextLine", JSObject, {
|
|
|
132
133
|
while (min < max){
|
|
133
134
|
mid = Math.floor(min + (max - min) / 2);
|
|
134
135
|
run = this._runs[mid];
|
|
135
|
-
i = run.range.location
|
|
136
|
+
i = run.range.location;
|
|
136
137
|
l = run.range.length;
|
|
137
138
|
if (index < i){
|
|
138
139
|
max = mid;
|
|
@@ -178,7 +179,7 @@ JSClass("JSTextLine", JSObject, {
|
|
|
178
179
|
},
|
|
179
180
|
|
|
180
181
|
copy: function(){
|
|
181
|
-
var line =
|
|
182
|
+
var line = this.$class.init();
|
|
182
183
|
line._origin = JSPoint(this._origin);
|
|
183
184
|
line._size = JSSize(this._size);
|
|
184
185
|
line._trailingWhitespaceWidth = this._trailingWhitespaceWidth;
|
|
@@ -39,7 +39,7 @@ JSClass("JSTextRun", JSObject, {
|
|
|
39
39
|
this.attachment = attributes[JSAttributedString.Attribute.attachment] || null;
|
|
40
40
|
if (this.attachment){
|
|
41
41
|
this.baseline = this.attachment.baselineAdjustment;
|
|
42
|
-
this._size = JSSize(this.attachment.size
|
|
42
|
+
this._size = JSSize(this.attachment.size);
|
|
43
43
|
}else{
|
|
44
44
|
this.font = font;
|
|
45
45
|
this.baseline = -font.descender;
|
|
@@ -113,7 +113,7 @@ JSClass("JSTextRun", JSObject, {
|
|
|
113
113
|
var glyph = null;
|
|
114
114
|
var width;
|
|
115
115
|
var characterLength;
|
|
116
|
-
var runningIndex =
|
|
116
|
+
var runningIndex = this.range.location;
|
|
117
117
|
for (var i = 0, l = this.glyphs.length; runningIndex < index && i < l; ++i){
|
|
118
118
|
glyph = this.glyphs[i];
|
|
119
119
|
width = this.font.widthOfGlyph(glyph);
|
|
@@ -130,11 +130,13 @@ JSClass("JSTextRun", JSObject, {
|
|
|
130
130
|
},
|
|
131
131
|
|
|
132
132
|
copy: function(){
|
|
133
|
-
var run =
|
|
133
|
+
var run = this.$class.init();
|
|
134
134
|
run._origin = JSPoint(this._origin);
|
|
135
135
|
run._size = JSSize(this._size);
|
|
136
|
+
run.baseline = this.baseline;
|
|
136
137
|
run._range = JSRange(this._range);
|
|
137
138
|
run.attributes = this.attributes;
|
|
139
|
+
run.attachment = this.attachment;
|
|
138
140
|
run.font = this.font;
|
|
139
141
|
run.glyphs = [];
|
|
140
142
|
run.glyphCharacterLengths = [];
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
// limitations under the License.
|
|
15
15
|
|
|
16
16
|
// #import "JSObject.js"
|
|
17
|
+
// #import "JSProtocol.js"
|
|
17
18
|
// #import "CoreTypes.js"
|
|
18
19
|
// #import "JSAttributedString.js"
|
|
19
20
|
// #import "JSFont.js"
|
|
@@ -26,6 +27,12 @@
|
|
|
26
27
|
|
|
27
28
|
var logger = JSLog("foundation", "text");
|
|
28
29
|
|
|
30
|
+
JSProtocol("JSTextTypesetterDelegate", JSProtocol, {
|
|
31
|
+
|
|
32
|
+
typesetterAttributesForStringAttributes: function(typesetter, stringAttributes){}
|
|
33
|
+
|
|
34
|
+
});
|
|
35
|
+
|
|
29
36
|
JSClass("JSTextTypesetter", JSObject, {
|
|
30
37
|
|
|
31
38
|
attributedString: JSDynamicProperty('_attributedString'),
|
|
@@ -68,7 +75,8 @@ JSClass("JSTextTypesetter", JSObject, {
|
|
|
68
75
|
|
|
69
76
|
_createEmptyLine: function(range){
|
|
70
77
|
var attributes = this._attributedString.attributesAtIndex(range.location);
|
|
71
|
-
|
|
78
|
+
attributes = this.resolveAttributes(attributes);
|
|
79
|
+
var font = attributes.font;
|
|
72
80
|
return JSTextLine.initWithHeight(font.lineHeight, -font.descender, range.location);
|
|
73
81
|
},
|
|
74
82
|
|
|
@@ -129,16 +137,18 @@ JSClass("JSTextTypesetter", JSObject, {
|
|
|
129
137
|
|
|
130
138
|
var iterator = this._attributedString.string.userPerceivedCharacterIterator(range.location);
|
|
131
139
|
var runIterator = this._attributedString.runIterator(range.location);
|
|
132
|
-
var initialLineAttributes = runIterator.attributes;
|
|
133
140
|
var codeIterator;
|
|
134
141
|
var attachment;
|
|
142
|
+
var attributes;
|
|
135
143
|
// TODO: support mask character (only important if we need to rasterize a password field, which isn't a major use case)
|
|
136
144
|
// Create run descriptors that at fill the line, maybe going a bit over
|
|
137
145
|
do {
|
|
138
146
|
|
|
147
|
+
attributes = this.resolveAttributes(runIterator.attributes);
|
|
148
|
+
|
|
139
149
|
// If we're at the start of a new run, make a new run descriptor
|
|
140
150
|
if (runDescriptor === null){
|
|
141
|
-
runDescriptor = JSTextTypesetterRunDescriptor(remainingRange.location,
|
|
151
|
+
runDescriptor = JSTextTypesetterRunDescriptor(remainingRange.location, attributes);
|
|
142
152
|
runDescriptors.push(runDescriptor);
|
|
143
153
|
preferredFont = runDescriptor.font;
|
|
144
154
|
}
|
|
@@ -146,9 +156,10 @@ JSClass("JSTextTypesetter", JSObject, {
|
|
|
146
156
|
// Check some properties of this character
|
|
147
157
|
newline = iterator.isMandatoryLineBreak;
|
|
148
158
|
printable = !newline && !iterator.isWhiteSpace;
|
|
159
|
+
attachment = runIterator.attachment;
|
|
149
160
|
|
|
150
161
|
// Adjust the run's font if needed
|
|
151
|
-
if (
|
|
162
|
+
if (attachment !== null){
|
|
152
163
|
// 1. Attachment font:
|
|
153
164
|
// Attachment runs use a special "attachment font" that can draw the attachment rather
|
|
154
165
|
// than draw a glyph. It's perhaps a little strange to call an attachment a font, but
|
|
@@ -156,7 +167,6 @@ JSClass("JSTextTypesetter", JSObject, {
|
|
|
156
167
|
// here is actually a specially drawn attachment. The attachment font has a single glyph
|
|
157
168
|
// that corresponds to the 0xFFFC attachment character code, whose width matches the
|
|
158
169
|
// attachment's width
|
|
159
|
-
attachment = runIterator.attributes[JSAttributedString.Attribute.attachment];
|
|
160
170
|
attachment.layout(preferredFont, width);
|
|
161
171
|
usedWidth += attachment.size.width;
|
|
162
172
|
runDescriptor.glyphs.push(0);
|
|
@@ -169,7 +179,7 @@ JSClass("JSTextTypesetter", JSObject, {
|
|
|
169
179
|
// If our preferred font has a glyph for the character, but we're in a run
|
|
170
180
|
// that is using a fallback font, create a new run, switching back to the preferred font
|
|
171
181
|
if (runDescriptor.font !== preferredFont){
|
|
172
|
-
runDescriptor = JSTextTypesetterRunDescriptor(remainingRange.location,
|
|
182
|
+
runDescriptor = JSTextTypesetterRunDescriptor(remainingRange.location, attributes);
|
|
173
183
|
runDescriptors.push(runDescriptor);
|
|
174
184
|
}
|
|
175
185
|
}else{
|
|
@@ -179,7 +189,7 @@ JSClass("JSTextTypesetter", JSObject, {
|
|
|
179
189
|
// If the fallback doesn't match our run, adjust the run or create a new run depending on if it's emtpy
|
|
180
190
|
if (fallbackFont !== runDescriptor.font){
|
|
181
191
|
if (runDescriptor.length > 0){
|
|
182
|
-
runDescriptor = JSTextTypesetterRunDescriptor(remainingRange.location,
|
|
192
|
+
runDescriptor = JSTextTypesetterRunDescriptor(remainingRange.location, attributes);
|
|
183
193
|
runDescriptors.push(runDescriptor);
|
|
184
194
|
}
|
|
185
195
|
runDescriptor.font = fallbackFont;
|
|
@@ -317,7 +327,27 @@ JSClass("JSTextTypesetter", JSObject, {
|
|
|
317
327
|
if (ranges.length === 0){
|
|
318
328
|
ranges.push(JSTextTypesetterPrintableRange(location, 0, 0));
|
|
319
329
|
}
|
|
320
|
-
}
|
|
330
|
+
},
|
|
331
|
+
|
|
332
|
+
delegate: null,
|
|
333
|
+
|
|
334
|
+
resolveAttributes: function(attributes){
|
|
335
|
+
if (attributes.font){
|
|
336
|
+
if (attributes.bold || attributes.italic){
|
|
337
|
+
attributes = JSCopy(attributes);
|
|
338
|
+
if (attributes.bold){
|
|
339
|
+
attributes.font = attributes.font.fontWithWeight(JSFont.Weight.bold);
|
|
340
|
+
}
|
|
341
|
+
if (attributes.italic){
|
|
342
|
+
attributes.font = attributes.font.fontWithStyle(JSFont.Style.italic);
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
if (this.delegate && this.delegate.typesetterAttributesForStringAttributes){
|
|
347
|
+
attributes = this.delegate.typesetterAttributesForStringAttributes(this, attributes);
|
|
348
|
+
}
|
|
349
|
+
return attributes;
|
|
350
|
+
},
|
|
321
351
|
|
|
322
352
|
});
|
|
323
353
|
|
|
@@ -329,8 +359,8 @@ var JSTextTypesetterRunDescriptor = function(location, attributes){
|
|
|
329
359
|
this.location = location.location;
|
|
330
360
|
this.attributes = location.attributes;
|
|
331
361
|
this.length = location.length;
|
|
332
|
-
this.glyphs = location.glyphs;
|
|
333
|
-
this.glyphCharacterLengths = location.glyphCharacterLengths;
|
|
362
|
+
this.glyphs = JSCopy(location.glyphs);
|
|
363
|
+
this.glyphCharacterLengths = JSCopy(location.glyphCharacterLengths);
|
|
334
364
|
this.font = location.font;
|
|
335
365
|
}else{
|
|
336
366
|
this.location = location;
|
|
@@ -338,7 +368,7 @@ var JSTextTypesetterRunDescriptor = function(location, attributes){
|
|
|
338
368
|
this.length = 0;
|
|
339
369
|
this.glyphs = [];
|
|
340
370
|
this.glyphCharacterLengths = [];
|
|
341
|
-
this.font =
|
|
371
|
+
this.font = attributes.font || null;
|
|
342
372
|
}
|
|
343
373
|
};
|
|
344
374
|
|
|
@@ -357,18 +387,4 @@ var JSTextTypesetterPrintableRange = function(location, length, width){
|
|
|
357
387
|
}
|
|
358
388
|
};
|
|
359
389
|
|
|
360
|
-
JSTextTypesetter.FontFromAttributes = function(attributes){
|
|
361
|
-
var font = attributes[JSAttributedString.Attribute.font];
|
|
362
|
-
if (!font){
|
|
363
|
-
return null;
|
|
364
|
-
}
|
|
365
|
-
if (attributes[JSAttributedString.Attribute.bold]){
|
|
366
|
-
font = font.fontWithWeight(JSFont.Weight.bold);
|
|
367
|
-
}
|
|
368
|
-
if (attributes[JSAttributedString.Attribute.italic]){
|
|
369
|
-
font = font.fontWithStyle(JSFont.Style.italic);
|
|
370
|
-
}
|
|
371
|
-
return font;
|
|
372
|
-
};
|
|
373
|
-
|
|
374
390
|
})();
|
|
@@ -244,6 +244,25 @@ Object.defineProperties(String.prototype, {
|
|
|
244
244
|
}
|
|
245
245
|
},
|
|
246
246
|
|
|
247
|
+
rangeForParagraphAtIndex: {
|
|
248
|
+
enumerable: false,
|
|
249
|
+
value: function String_rangeForParagraphAtIndex(index){
|
|
250
|
+
var iterator1 = this.userPerceivedCharacterIterator(index);
|
|
251
|
+
var startIndex = iterator1.range.location;
|
|
252
|
+
var iterator2 = UserPerceivedCharacterIterator(iterator1);
|
|
253
|
+
iterator1.decrement();
|
|
254
|
+
while (!iterator1.isParagraphBreak){
|
|
255
|
+
startIndex = iterator1.range.location;
|
|
256
|
+
iterator1.decrement();
|
|
257
|
+
}
|
|
258
|
+
while (!iterator2.isParagraphBreak){
|
|
259
|
+
iterator2.increment();
|
|
260
|
+
}
|
|
261
|
+
var endIndex = iterator2.range.end;
|
|
262
|
+
return JSRange(startIndex, endIndex - startIndex);
|
|
263
|
+
}
|
|
264
|
+
},
|
|
265
|
+
|
|
247
266
|
// -------------------------------------------------------------------------
|
|
248
267
|
// MARK: - Changing a String
|
|
249
268
|
|
|
@@ -1159,6 +1178,22 @@ Object.defineProperties(UserPerceivedCharacterIterator.prototype, {
|
|
|
1159
1178
|
// can start with CR, we know the second character must be LF.
|
|
1160
1179
|
return this._unicodeIterator.character.isLineBreak;
|
|
1161
1180
|
}
|
|
1181
|
+
},
|
|
1182
|
+
|
|
1183
|
+
isParagraphBreak: {
|
|
1184
|
+
enumerable: true,
|
|
1185
|
+
configurable: false,
|
|
1186
|
+
get: function UserPerceivedCharacterIterator_isMandatoryLineBreak(){
|
|
1187
|
+
if (this._unicodeIterator.character === null){
|
|
1188
|
+
return true;
|
|
1189
|
+
}
|
|
1190
|
+
// Note that even though we're only checking the first character,
|
|
1191
|
+
// it will match the multi-char CRLF sequence, which is considered
|
|
1192
|
+
// to be a single user perceived character. In this case, only
|
|
1193
|
+
// the CR is checked, but since no other user perceived character
|
|
1194
|
+
// can start with CR, we know the second character must be LF.
|
|
1195
|
+
return this._unicodeIterator.character.isParagraphBreak;
|
|
1196
|
+
}
|
|
1162
1197
|
}
|
|
1163
1198
|
|
|
1164
1199
|
});
|
|
@@ -778,6 +778,14 @@ UnicodeChar.prototype = Object.create(Object.prototype, {
|
|
|
778
778
|
Object.defineProperty(this, 'isLineBreak', {value: this.code === UnicodeChar.LineBreaks.newLine || this.code === UnicodeChar.LineBreaks.formFeed || this.code === UnicodeChar.LineBreaks.carriageReturn || this.code === UnicodeChar.LineBreaks.nextLine || this.code === UnicodeChar.LineBreaks.lineSeparator || this.code === UnicodeChar.LineBreaks.paragraphSeparator});
|
|
779
779
|
return this.isLineBreak;
|
|
780
780
|
}
|
|
781
|
+
},
|
|
782
|
+
|
|
783
|
+
isParagraphBreak: {
|
|
784
|
+
configurable: true,
|
|
785
|
+
get: function UnicodeChar_lazy_isParagraphBreak(){
|
|
786
|
+
Object.defineProperty(this, 'isParagraphBreak', {value: this.code === UnicodeChar.LineBreaks.newLine || this.code === UnicodeChar.LineBreaks.formFeed || this.code === UnicodeChar.LineBreaks.carriageReturn || this.code === UnicodeChar.LineBreaks.nextLine || this.code === UnicodeChar.LineBreaks.paragraphSeparator});
|
|
787
|
+
return this.isParagraphBreak;
|
|
788
|
+
}
|
|
781
789
|
}
|
|
782
790
|
|
|
783
791
|
});
|
|
@@ -3,7 +3,7 @@ JSBundle.bundles['io.breakside.JSKit.Foundation'] = {
|
|
|
3
3
|
"Info": {
|
|
4
4
|
"JSBundleType": "framework",
|
|
5
5
|
"JSBundleIdentifier": "io.breakside.JSKit.Foundation",
|
|
6
|
-
"JSBundleVersion": "2022.
|
|
6
|
+
"JSBundleVersion": "2022.24.0",
|
|
7
7
|
"JSDevelopmentLanguage": "en",
|
|
8
8
|
"JSCopyright": "Copyright © 2020 Breakside Inc.",
|
|
9
9
|
"JSBundleEnvironments": {
|
|
@@ -11,7 +11,7 @@ JSBundle.bundles['io.breakside.JSKit.Foundation'] = {
|
|
|
11
11
|
"node": "Foundation+Node.js"
|
|
12
12
|
},
|
|
13
13
|
"JSLicenseNotice": "Licensed under the Breakside Public License, Version 1.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nIf a copy of the License was not distributed with this file, you may\nobtain a copy at\n\n http://breakside.io/licenses/LICENSE-1.0.txt\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n",
|
|
14
|
-
"GitRevision": "
|
|
14
|
+
"GitRevision": "384e6ac71d317262e4518508066ab61322409ba2"
|
|
15
15
|
},
|
|
16
16
|
"Resources": [
|
|
17
17
|
{
|
|
@@ -65,6 +65,7 @@
|
|
|
65
65
|
"JSContext.js",
|
|
66
66
|
"JSDateFormatter.js",
|
|
67
67
|
"JSNumberFormatter.js",
|
|
68
|
+
"JSParagraphStyle.js",
|
|
68
69
|
"JSTextLayoutManager.js",
|
|
69
70
|
"JSTextRun.js",
|
|
70
71
|
"JSTextLine.js",
|
|
@@ -225,9 +226,11 @@
|
|
|
225
226
|
"JSContext",
|
|
226
227
|
"JSDateFormatter",
|
|
227
228
|
"JSNumberFormatter",
|
|
229
|
+
"JSParagraphStyle",
|
|
228
230
|
"JSTextLayoutManager",
|
|
229
231
|
"JSTextRun",
|
|
230
232
|
"JSTextLine",
|
|
233
|
+
"JSTextTypesetterDelegate",
|
|
231
234
|
"JSTextTypesetter",
|
|
232
235
|
"JSTextFrame",
|
|
233
236
|
"JSTextFramesetter",
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"JSBundleType": "framework",
|
|
3
3
|
"JSBundleIdentifier": "io.breakside.JSKit.SecurityKit",
|
|
4
|
-
"JSBundleVersion": "2022.
|
|
4
|
+
"JSBundleVersion": "2022.24.0",
|
|
5
5
|
"JSDevelopmentLanguage": "en",
|
|
6
6
|
"JSCopyright": "Copyright © 2020 Breakside Inc.",
|
|
7
7
|
"JSBundleEnvironments": {
|
|
@@ -9,5 +9,5 @@
|
|
|
9
9
|
"node": "SecurityKit+Node.js"
|
|
10
10
|
},
|
|
11
11
|
"JSLicenseNotice": "Licensed under the Breakside Public License, Version 1.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nIf a copy of the License was not distributed with this file, you may\nobtain a copy at\n\n http://breakside.io/licenses/LICENSE-1.0.txt\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n",
|
|
12
|
-
"GitRevision": "
|
|
12
|
+
"GitRevision": "384e6ac71d317262e4518508066ab61322409ba2"
|
|
13
13
|
}
|
|
@@ -3,7 +3,7 @@ JSBundle.bundles['io.breakside.JSKit.SecurityKit'] = {
|
|
|
3
3
|
"Info": {
|
|
4
4
|
"JSBundleType": "framework",
|
|
5
5
|
"JSBundleIdentifier": "io.breakside.JSKit.SecurityKit",
|
|
6
|
-
"JSBundleVersion": "2022.
|
|
6
|
+
"JSBundleVersion": "2022.24.0",
|
|
7
7
|
"JSDevelopmentLanguage": "en",
|
|
8
8
|
"JSCopyright": "Copyright © 2020 Breakside Inc.",
|
|
9
9
|
"JSBundleEnvironments": {
|
|
@@ -11,7 +11,7 @@ JSBundle.bundles['io.breakside.JSKit.SecurityKit'] = {
|
|
|
11
11
|
"node": "SecurityKit+Node.js"
|
|
12
12
|
},
|
|
13
13
|
"JSLicenseNotice": "Licensed under the Breakside Public License, Version 1.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nIf a copy of the License was not distributed with this file, you may\nobtain a copy at\n\n http://breakside.io/licenses/LICENSE-1.0.txt\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n",
|
|
14
|
-
"GitRevision": "
|
|
14
|
+
"GitRevision": "384e6ac71d317262e4518508066ab61322409ba2"
|
|
15
15
|
},
|
|
16
16
|
"Resources": [],
|
|
17
17
|
"ResourceLookup": {
|
package/Info.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"JSBundleType": "node",
|
|
3
3
|
"JSBundleIdentifier": "io.breakside.jskit",
|
|
4
|
-
"JSBundleVersion": "2022.
|
|
4
|
+
"JSBundleVersion": "2022.24.0",
|
|
5
5
|
"JSExecutableName": "jskit",
|
|
6
6
|
"NPMOrganization": "breakside",
|
|
7
7
|
"JSResources": [
|
|
8
8
|
"Tests/HTMLTestRunner.js",
|
|
9
9
|
"Tests/NodeTestRunner.js"
|
|
10
10
|
],
|
|
11
|
-
"GitRevision": "
|
|
11
|
+
"GitRevision": "384e6ac71d317262e4518508066ab61322409ba2"
|
|
12
12
|
}
|
|
@@ -3,14 +3,14 @@ JSBundle.bundles['io.breakside.jskit'] = {
|
|
|
3
3
|
"Info": {
|
|
4
4
|
"JSBundleType": "node",
|
|
5
5
|
"JSBundleIdentifier": "io.breakside.jskit",
|
|
6
|
-
"JSBundleVersion": "2022.
|
|
6
|
+
"JSBundleVersion": "2022.24.0",
|
|
7
7
|
"JSExecutableName": "jskit",
|
|
8
8
|
"NPMOrganization": "breakside",
|
|
9
9
|
"JSResources": [
|
|
10
10
|
"Tests/HTMLTestRunner.js",
|
|
11
11
|
"Tests/NodeTestRunner.js"
|
|
12
12
|
],
|
|
13
|
-
"GitRevision": "
|
|
13
|
+
"GitRevision": "384e6ac71d317262e4518508066ab61322409ba2"
|
|
14
14
|
},
|
|
15
15
|
"Resources": [
|
|
16
16
|
{
|