Merge remote-tracking branch 'upstream/main' into Aristo3

This commit is contained in:
daboe01 committed 2026-06-20 12:37:15 +02:00
commit aa592b8bc6
8 files changed
+467 -56

No files matched your search

+48 -12
View File
@@ -31,8 +31,9 @@
@import "CPFont.j"
@global _MakeRangeFromAbs
@global document
@global CPBaselineOffsetAttributeName
@global CPSuperscriptAttributeName
@class CPTextContainer
@class CPTextView
@@ -964,16 +965,11 @@ _oncontextmenuhandler = function () { return false; };
if (frame)
{
var correctedRect = CGRectCreateCopy(frame);
correctedRect.size.height -= frame._descent;
correctedRect.origin.y -= frame._descent;
if (!rect)
rect = CGRectCreateCopy(correctedRect);
else
rect = CGRectUnion(rect, correctedRect);
if (_isNewlineCharacter([[_textStorage string] characterAtIndex:MAX(0, CPMaxRange(selectedCharRange) - 1)]))
rect.size.width = containerSize.width - rect.origin.x;
}
}
}
@@ -1225,7 +1221,7 @@ var _objectsInRange = function(aList, aRange)
if (![attributes objectForKey:_CPAttachmentInvisible])
{
var view = [attributes objectForKey:_CPAttachmentView];
var run = {_range:CPMakeRangeCopy(effectiveRange), color:nil, font:nil, elem:nil, string:nil, view:view, paragraphStyle:paragraphStyle};
var run = {_range:CPMakeRangeCopy(effectiveRange), color:nil, font:nil, elem:nil, string:nil, view:view, paragraphStyle:paragraphStyle, underline:underline, baselineOffset:0.0};
_runs.push(run);
}
}
@@ -1235,6 +1231,34 @@ var _objectsInRange = function(aList, aRange)
bgcolor = [attributes objectForKey:CPBackgroundColorAttributeName],
font = [attributes objectForKey:CPFontAttributeName] || [textStorage font] || [CPFont systemFontOfSize:12.0];
var baselineOffset = [attributes objectForKey:CPBaselineOffsetAttributeName],
superscript = [attributes objectForKey:CPSuperscriptAttributeName];
if (baselineOffset === nil || baselineOffset === undefined || typeof baselineOffset !== "number")
baselineOffset = 0.0;
if (superscript === nil || superscript === undefined || typeof superscript !== "number")
superscript = 0;
if (superscript !== 0)
{
var size = [font size],
scaledSize = size * 0.65,
fontName = [font familyName],
isBold = [font isBold],
isItalic = [font isItalic];
font = [CPFont _fontWithName:fontName size:scaledSize bold:isBold italic:isItalic];
if (baselineOffset === 0.0)
{
if (superscript > 0)
baselineOffset = size * 0.35;
else
baselineOffset = -size * 0.15;
}
}
var currentLoc = effectiveRange.location,
strLen = string.length,
startIdx = 0;
@@ -1254,7 +1278,9 @@ var _objectsInRange = function(aList, aRange)
elem: nil,
string: subString,
bgcolor: bgcolor,
paragraphStyle: paragraphStyle
paragraphStyle: paragraphStyle,
underline: underline,
baselineOffset: baselineOffset
};
_runs.push(run);
}
@@ -1267,7 +1293,9 @@ var _objectsInRange = function(aList, aRange)
elem: nil,
string: nil,
bgcolor: nil,
paragraphStyle: paragraphStyle
paragraphStyle: paragraphStyle,
underline: underline,
baselineOffset: 0.0
};
_runs.push(tabRun);
@@ -1286,7 +1314,9 @@ var _objectsInRange = function(aList, aRange)
elem: nil,
string: subString,
bgcolor: bgcolor,
paragraphStyle: paragraphStyle
paragraphStyle: paragraphStyle,
underline: underline,
baselineOffset: baselineOffset
};
_runs.push(run);
}
@@ -1313,7 +1343,10 @@ var _objectsInRange = function(aList, aRange)
{
_glyphsFrames[i] = CGRectMake(origin.x, origin.y, someAdvancements[i].width, height);
_glyphsFrames[i]._descent = someAdvancements[i].descent;
_glyphsOffsets[i] = height - someAdvancements[i].height;
// Align the run's baseline with the common line baseline (_location.y)
_glyphsOffsets[i] = _location.y - someAdvancements[i].height;
origin.x += someAdvancements[i].width;
}
}
@@ -1458,7 +1491,10 @@ var _objectsInRange = function(aList, aRange)
if (!_RectEqualToRectHorizontally(newLineFragment._fragmentRect, _fragmentRect))
return NO;
if (newFragmentRuns[i].color !== oldFragmentRuns[i].color || newFragmentRuns[i].bgcolor !== oldFragmentRuns[i].bgcolor || newFragmentRuns[i].font !== oldFragmentRuns[i].font)
if (newFragmentRuns[i].color !== oldFragmentRuns[i].color ||
newFragmentRuns[i].bgcolor !== oldFragmentRuns[i].bgcolor ||
newFragmentRuns[i].font !== oldFragmentRuns[i].font ||
newFragmentRuns[i].baselineOffset !== oldFragmentRuns[i].baselineOffset)
return NO;
var oldStyle = oldFragmentRuns[i].paragraphStyle || [CPParagraphStyle defaultParagraphStyle],
+46 -13
View File
@@ -148,27 +148,58 @@ CPLineMovesUp = 4;
- (void)setWidthTracksTextView:(BOOL)flag
{
_widthTracksTextView = flag;
[_textView setPostsFrameChangedNotifications:flag];
if (_widthTracksTextView === flag)
return;
if (flag && _textView)
{
[[CPNotificationCenter defaultCenter] addObserver:self
selector:@selector(textViewFrameChanged:)
name:CPViewFrameDidChangeNotification
object:_textView];
}
else
_widthTracksTextView = flag;
[self _updateFrameObserver];
}
// Controls whether the receiver adjusts the height of its bounding rectangle when its text view is resized.
- (BOOL)heightTracksTextView
{
return _heightTracksTextView;
}
- (void)setHeightTracksTextView:(BOOL)flag
{
if (_heightTracksTextView === flag)
return;
_heightTracksTextView = flag;
[self _updateFrameObserver];
}
- (void)_updateFrameObserver
{
if (_textView)
{
[[CPNotificationCenter defaultCenter] removeObserver:self
name:CPViewFrameDidChangeNotification
object:_textView];
var flag = _widthTracksTextView || _heightTracksTextView;
[_textView setPostsFrameChangedNotifications:flag];
if (flag)
{
[[CPNotificationCenter defaultCenter] addObserver:self
selector:@selector(textViewFrameChanged:)
name:CPViewFrameDidChangeNotification
object:_textView];
}
}
}
- (void)textViewFrameChanged:(CPNotification)aNotification
{
var newSize = CGSizeMake([_textView frame].size.width, _size.height);
var newSize = CGSizeMake(_size.width, _size.height);
if (_widthTracksTextView)
newSize.width = [_textView frame].size.width;
if (_heightTracksTextView)
newSize.height = [_textView frame].size.height;
[self setContainerSize:newSize];
}
@@ -177,7 +208,9 @@ CPLineMovesUp = 4;
{
if (_textView)
{
[self setWidthTracksTextView:NO]; // We only support width
[[CPNotificationCenter defaultCenter] removeObserver:self
name:CPViewFrameDidChangeNotification
object:_textView];
[_textView setTextContainer:nil];
}
@@ -185,7 +218,7 @@ CPLineMovesUp = 4;
if (_textView)
{
[self setWidthTracksTextView:_widthTracksTextView]; // We only support width
[self _updateFrameObserver];
[_textView setTextContainer:self];
}
+30 -6
View File
@@ -1010,6 +1010,25 @@ Sets the selection to a range of characters in response to user action.
if (doOverwrite && _placeholderString == nil && isNewSelection)
[self setTypingAttributes:[_textStorage attributesAtIndex:CPMaxRange(range) effectiveRange:nil]];
// Update the shared CPColorPanel with the active selection color
if ([self _isFirstResponder] && [_textStorage length] > 0)
{
var currentTextColor = [self textColor] || [CPColor blackColor];
if ([self isRichText])
{
var charIndex = _selectionRange.location;
if (charIndex >= [_textStorage length])
charIndex = MAX(0, charIndex - 1);
var attributes = [_textStorage attributesAtIndex:charIndex effectiveRange:nil];
if ([attributes objectForKey:CPForegroundColorAttributeName])
currentTextColor = [attributes objectForKey:CPForegroundColorAttributeName];
}
[[CPColorPanel sharedColorPanel] setColor:currentTextColor];
}
[[CPNotificationCenter defaultCenter] postNotificationName:CPTextViewDidChangeSelectionNotification object:self];
}
@@ -1804,6 +1823,13 @@ Sets the selection to a range of characters in response to user action.
// SYNCHRONIZE ACTIVE PARAGRAPH MARKERS ON TYPING ATTRIBUTES CHANGE
[self updateRuler];
// Synchronize CPColorPanel if text view is active
if ([self _isFirstResponder])
{
var currentTextColor = [_typingAttributes objectForKey:CPForegroundColorAttributeName] || [self textColor] || [CPColor blackColor];
[[CPColorPanel sharedColorPanel] setColor:currentTextColor];
}
[[CPNotificationCenter defaultCenter] postNotificationName:CPTextViewDidChangeTypingAttributesNotification object:self];
// We always clear the saved selection range from the last mouse down event here.
@@ -2316,17 +2342,15 @@ Sets the selection to a range of characters in response to user action.
var loc = (_selectionRange.location == numberOfGlyphs) ? _selectionRange.location - 1 : _selectionRange.location,
caretOffset = [_layoutManager _characterOffsetAtLocation:loc],
oldYPosition = CGRectGetMaxY(caretRect),
caretDescend = [_layoutManager _descentAtLocation:loc];
font = [_textStorage attribute:CPFontAttributeName atIndex:loc effectiveRange:nil] || [self font];
if (caretOffset > 0)
{
caretRect.origin.y += caretOffset;
caretRect.size.height = oldYPosition - caretRect.origin.y;
}
if (caretDescend < 0)
caretRect.size.height -= caretDescend;
// Set the caret height to match the size of the active font
caretRect.size.height = [font size];
if (_selectionRange.location == numberOfGlyphs)
caretRect.origin.x += caretRect.size.width;
@@ -2335,7 +2359,7 @@ Sets the selection to a range of characters in response to user action.
caretRect.origin.y += _textContainerOrigin.y;
caretRect.size.width = MAX(1.0, caretRect.size.width);
caretRect.size.height = MAX(1.0, caretRect.size.height);
caretRect.size.height = MAX(1.0, caretRect.size.height) + 2;
return caretRect;
}
+47 -8
View File
@@ -30,6 +30,9 @@
@import "CPTextStorage.j"
@import "CPFont.j"
@global CPBaselineOffsetAttributeName
@global CPSuperscriptAttributeName
// forward declare these classes for type matching
@class CPLayoutManager
@class CPTextContainer
@@ -353,9 +356,41 @@ var CPSystemTypesetterFactory,
if (!currentFont)
currentFont = [_textStorage font] || [CPFont systemFontOfSize:12.0];
ascent = [currentFont ascender];
descent = [currentFont descender];
leading = (ascent - descent) * 0.2; // FAKE leading
// Safely retrieve and validate CPBaselineOffsetAttributeName
var baselineOffset = [_currentAttributes objectForKey:CPBaselineOffsetAttributeName];
if (baselineOffset === nil || baselineOffset === undefined || typeof baselineOffset !== "number")
baselineOffset = 0.0;
// Safely retrieve and validate CPSuperscriptAttributeName
var superscript = [_currentAttributes objectForKey:CPSuperscriptAttributeName];
if (superscript === nil || superscript === undefined || typeof superscript !== "number")
superscript = 0;
if (superscript !== 0)
{
var size = [currentFont size],
scaledSize = size * 0.65,
fontName = [currentFont familyName],
isBold = [currentFont isBold],
isItalic = [currentFont isItalic];
currentFont = [CPFont _fontWithName:fontName size:scaledSize bold:isBold italic:isItalic];
if (baselineOffset === 0.0)
{
if (superscript > 0)
baselineOffset = size * 0.35;
else
baselineOffset = -size * 0.15;
}
}
var fontAscent = [currentFont ascender] || 0.0,
fontDescent = [currentFont descender] || 0.0;
ascent = fontAscent + baselineOffset;
descent = fontDescent + baselineOffset;
leading = (fontAscent - fontDescent) * 0.2; // FAKE leading
currentFontLineHeight = ascent - descent + leading;
@@ -368,11 +403,15 @@ var CPSystemTypesetterFactory,
}
if (currentFontLineHeight > _lineHeight)
_lineHeight = currentFontLineHeight;
// Clean bounds logic to prevent NaN and layout calculation overhead
var currentAscent = (ascent === undefined || isNaN(ascent)) ? 0.0 : ascent,
currentLineHeight = (currentFontLineHeight === undefined || isNaN(currentFontLineHeight)) ? 12.0 : currentFontLineHeight;
if (ascent > _lineBase)
_lineBase = ascent;
if (currentLineHeight > _lineHeight)
_lineHeight = currentLineHeight;
if (currentAscent > _lineBase)
_lineBase = currentAscent;
lineRange.length++;
measuringRange.length++;
@@ -546,7 +585,7 @@ var CPSystemTypesetterFactory,
isNewline = NO;
_lineFragments = [];
_lineHeight = 0;
_lineBase = ascent;
_lineBase = 0;
isStartOfPhysicalLine = YES;
}
+76 -6
View File
@@ -38,6 +38,8 @@
@global CPParagraphStyleAttributeName
@global CPAttachmentAttributeName
@global CPUnderlineStyleAttributeName
@global CPBaselineOffsetAttributeName
@global CPSuperscriptAttributeName
@global CPLeftTabStopType
@global CPRightTabStopType
@@ -72,6 +74,8 @@ var cp1252Map = {
BOOL script;
BOOL _tabChanged;
CPTabStopType _nextTabType;
int superscript;
float baselineOffset;
}
- (id)init
@@ -104,6 +108,8 @@ var cp1252Map = {
mynew.ulColour = ulColour;
mynew._tabChanged = _tabChanged;
mynew._nextTabType = _nextTabType;
mynew.superscript = superscript;
mynew.baselineOffset = baselineOffset;
return mynew;
}
@@ -169,6 +175,8 @@ var cp1252Map = {
underline = 0;
strikethrough = 0;
script = 0;
superscript = 0;
baselineOffset = 0.0;
}
- (void)addTab:(float)location type:(CPTextTabType)type
@@ -212,6 +220,12 @@ var cp1252Map = {
if (underline)
[ret setObject:[CPNumber numberWithInt:1] forKey:CPUnderlineStyleAttributeName];
if (superscript !== 0)
[ret setObject:[CPNumber numberWithInt:superscript] forKey:CPSuperscriptAttributeName];
if (baselineOffset !== 0.0)
[ret setObject:[CPNumber numberWithFloat:baselineOffset] forKey:CPBaselineOffsetAttributeName];
return ret;
}
@end
@@ -226,6 +240,12 @@ var kRgsymRtf = {
"b" : [ "b", 1, false, kRTFParserType_prop, "propBold"],
"ul" : [ "ul", 1, false, kRTFParserType_prop, "propUnderline"],
"i" : [ "i", 1, false, kRTFParserType_prop, "propItalic"],
"super" : [ "super", 1, true, kRTFParserType_prop, "propSuper"],
"sub" : [ "sub", 1, true, kRTFParserType_prop, "propSub"],
"nosupersub" : [ "nosupersub",1, true, kRTFParserType_prop, "propNoSuperSub"],
"up" : [ "up", 6, false, kRTFParserType_prop, "propUp"],
"dn" : [ "dn", 6, false, kRTFParserType_prop, "propDn"],
"plain" : [ "plain", 0, false, kRTFParserType_prop, "propPlain"],
"pgnucltr" : [ "pgnucltr", "pgULtr", true, kRTFParserType_prop, "propPgnFormat"],
"pgnlcltr" : [ "pgnlcltr", "pgLLtr", true, kRTFParserType_prop, "propPgnFormat"],
"qc" : [ "qc", "justC", true, kRTFParserType_prop, "propJust"],
@@ -314,6 +334,7 @@ var kRgsymRtf = {
CPArray _fontArray;
CPString _freename;
BOOL _parsingFontTable;
BOOL _keywordIsControlWord;
// Table parsing state
BOOL _inTableActive;
@@ -333,6 +354,7 @@ var kRgsymRtf = {
_states = [];
_currentParseIndex = 0;
_hexreturn = NO;
_keywordIsControlWord = NO;
_result = [CPAttributedString new];
_colorArray = [];
_fontArray = ['Arial']; // FIXME: should be name of system font
@@ -621,6 +643,36 @@ var kRgsymRtf = {
}
break;
case "super":
[self _flushCurrentRun];
_currentRun.superscript = 1;
break;
case "sub":
[self _flushCurrentRun];
_currentRun.superscript = -1;
break;
case "nosupersub":
[self _flushCurrentRun];
_currentRun.superscript = 0;
break;
case "up":
[self _flushCurrentRun];
_currentRun.baselineOffset = parseFloat(param) / 2.0;
break;
case "dn":
[self _flushCurrentRun];
_currentRun.baselineOffset = -parseFloat(param) / 2.0;
break;
case "plain":
[self _flushCurrentRun];
[_currentRun resetFont];
break;
case "qc": // paragraph center
[_currentRun.paragraph setAlignment:CPCenterTextAlignment];
break;
@@ -745,10 +797,15 @@ var kRgsymRtf = {
[self _flushCurrentRun];
var fontIndex = parseInt(param) - 1;
if (_currentRun && fontIndex >= 0)
_currentRun.fgColour = _colorArray[fontIndex];
if (_currentRun)
{
if (fontIndex >= 0 && fontIndex < _colorArray.length)
_currentRun.fgColour = _colorArray[fontIndex];
else
_currentRun.fgColour = nil;
}
break;
break;
case "cb": // change background color
case "highlight":
@@ -839,7 +896,12 @@ var kRgsymRtf = {
ch = rtf.charAt(_currentParseIndex);
if (!/[a-zA-Z]/.test(ch))
{
_keywordIsControlWord = NO;
return [self _translateKeyword:ch parameter:nil fParameter:fParam];
}
_keywordIsControlWord = YES;
while (new RegExp("[a-zA-Z]").test(ch))
{
@@ -922,12 +984,17 @@ var kRgsymRtf = {
else
{
_freename += tmp;
[self _appendPlainString:tmp];
// Only append literal spaces to the document if we are in the active body state
if (_curState == 0)
{
[self _appendPlainString:tmp];
}
}
break;
case "{":
lastchar = 0;
if (_waitingForNextRow)
[self _flushTableIfAny];
@@ -936,6 +1003,7 @@ var kRgsymRtf = {
break;
case "}":
lastchar = 0;
if (_waitingForNextRow)
[self _flushTableIfAny];
@@ -959,14 +1027,15 @@ var kRgsymRtf = {
_freename = '';
ch = [self _parseKeyword:rtf length:len];
if (!_hexreturn && ch.length == 0)
if (!_hexreturn && _keywordIsControlWord)
lastchar = 1;
else
lastchar = 0;
if (_hexreturn)
{
if (ch.length > 0)
// Only append decoded characters if we are in the active body state
if (ch.length > 0 && _curState === 0)
{
var byteVal = parseInt(ch, 16);
var unicodeVal = byteVal;
@@ -990,6 +1059,7 @@ var kRgsymRtf = {
case 0x0a:
case '\n':
case '\r':
lastchar = 0;
break;
default:
+24 -11
View File
@@ -542,6 +542,17 @@ function _points2twips(a) { return (a) * 20.0; }
cellText = "";
}
// Safely extract text representation from CPAttributedString / CPTextStorage if present
if (cellText && typeof cellText === "object") {
if (typeof cellText.string === "function") {
cellText = [cellText string];
} else if (cellText._string !== undefined) {
cellText = cellText._string;
} else if (cellText.string !== undefined) {
cellText = cellText.string;
}
}
cellText = String(cellText);
cellText = cellText.replace(/\\/g, '\\\\');
cellText = cellText.replace(/{/g, '\\{');
@@ -646,28 +657,29 @@ function _points2twips(a) { return (a) * 20.0; }
else if ([currAttrib isEqualToString:CPUnderlineStyleAttributeName])
{
headerString += @"\\ul";
trailerString += @"\\ulnone";
trailerString += @"\\ulnone "; // trailing space important!
}
else if ([currAttrib isEqualToString:CPSuperscriptAttributeName])
{
var value = [attributes objectForKey:CPSuperscriptAttributeName],
svalue = [value intValue] * 6;
ivalue = [value intValue];
if (svalue > 0)
if (ivalue > 0)
{
headerString += [CPString stringWithFormat:@"\\up%d", svalue];
trailerString += @"\\up0";
headerString += @"\\super";
trailerString += @"\\nosupersub "; // trailing space important!
}
else if (svalue < 0)
else if (ivalue < 0)
{
headerString += [CPString stringWithFormat:@"\\dn-%d", svalue];
trailerString += @"\\dn0";
headerString += @"\\sub";
trailerString += @"\\nosupersub "; // trailing space important!
}
}
else if ([currAttrib isEqualToString:CPBaselineOffsetAttributeName])
{
var value = [attributes objectForKey:CPBaselineOffsetAttributeName],
svalue = [value floatValue] * 2;
fvalue = [value floatValue],
svalue = Math.round(fvalue * 2.0); // Convert standard points to RTF half-points
if (svalue > 0)
{
@@ -676,7 +688,8 @@ function _points2twips(a) { return (a) * 20.0; }
}
else if (svalue < 0)
{
headerString += [CPString stringWithFormat:@"\\dn-%d", svalue];
// Correct negative formatting using safe positive boundary
headerString += [CPString stringWithFormat:@"\\dn%d", Math.abs(svalue)];
trailerString += @"\\dn0";
}
}
@@ -713,7 +726,7 @@ function _points2twips(a) { return (a) * 20.0; }
var nobraces;
if ([headerString length])
nobraces = [CPString stringWithFormat:@"%@ %@}", headerString, substring];
nobraces = [CPString stringWithFormat:@"%@ %@", headerString, substring];
else
nobraces = substring;