I have these two methods below: def _apply_forced_newlines(self, cursor): doc = self.text.document() corrections = [] # Position -> action ('insert') block = doc.firstBlock() while block.isValid(): layout = block.layout() if not layout: block = block.next() continue for line_index in range(layout.lineCount()): line = layout.lineAt(line_index) if not line.isValid(): continue # Calculate the absolute position at the end of the visual line line_end_pos_abs = block.position() + line.textStart() + line.textLength() if line_end_pos_abs < doc.characterCount() and line_end_pos_abs > 0: char_at_end = doc.characterAt(line_end_pos_abs - 1) char_after_end = doc.characterAt(line_end_pos_abs) if not char_at_end.isspace() and not char_after_end.isspace(): cursor.setPosition(line_end_pos_abs) cursor.movePosition(QTextCursor.StartOfWord) word_start_position = cursor.position() # Check that a newline isn't already there. if doc.characterAt(word_start_position - 1) != QChar('\u2028'): corrections.append((word_start_position, 'insert')) block = block.next() # Apply corrections if corrections: # Sort changes: only insertions, in descending position. corrections.sort(key=lambda item: -item[0]) print('corrections=', corrections) for position, action in corrections: if action == 'insert': forced_newline_format = QTextCharFormat() forced_newline_format.setObjectType(WritingSettings.SVG_CHAR_OBJECT_TYPE) forced_newline_format.setProperty(WritingSettings.FORCED_NEWLINE, 1) cursor.setPosition(position) cursor.insertText('\u2028', forced_newline_format) # Log to verify the property is set correctly char_format = cursor.charFormat() logging.info(f"Inserted newline at {position} with property: {char_format.property(WritingSettings.FORCED_NEWLINE)}") and: def _clear_forced_newlines(self, text_edit): doc = text_edit.document() # Define the property key forced_newline_property = WritingSettings.FORCED_NEWLINE cursor = QTextCursor(doc) cursor.beginEditBlock() # Iterate through blocks from the end to the beginning. block = doc.lastBlock() while block.isValid(): # Create a cursor for the current block block_cursor = QTextCursor(block) block_cursor.movePosition(QTextCursor.EndOfBlock) # Iterate through characters within the block from the end. while block_cursor.positionInBlock() > 0: # Select the character by moving back one position with KeepAnchor block_cursor.movePosition(QTextCursor.PreviousCharacter, QTextCursor.KeepAnchor) # Check if a character was selected if block_cursor.hasSelection(): selected_text = block_cursor.selectedText() char_format = block_cursor.charFormat() has_property = char_format.property(forced_newline_property) logging.info(f"remove Checking character at position {block_cursor.selectionStart()}: " f"selected_text='{repr(selected_text)}', " f"has_property={has_property}") # Check if the selected character is a newline and has the custom property if selected_text == '\u2028' and has_property == 1: logging.info('Removing forced newline at position: {}'.format(block_cursor.selectionStart())) # Remove the formatted newline and insert a space block_cursor.removeSelectedText() block_cursor.insertText(" ") # Move the cursor back one position to clear the selection for the next iteration block_cursor.movePosition(QTextCursor.PreviousCharacter) block = block.previous() cursor.endEditBlock() My log gives in the _apply_forced_newlines gives me below line: Inserted newline at 22 with property: 1 That is the property is set on the correct position where the newline char is inserted. But the log in _clear_forced_newlines gives me: remove Checking character at position 23: selected_text=''\ufffc'', has_property=1 remove Checking character at position 22: selected_text=''\u2028'', has_property=None The property is shown on the wrong position although the inserted char is at the correct position. How do I make sure to get the property set on the correct char at the correct position? Continue reading...