Closed danielallsopp closed 6 months ago
hi @danielallsopp,
this is by design in iOS. In attributedString
representation, each new line is a start of a new paragraph. I am not sure I understand your question correctly as for both the cases, you should be getting a new paragraph for each line of the text. I verified this again using the following in my tests:
var list = [ListItem]()
let indent: CGFloat = 50
list.append(ListItem(text: NSAttributedString(string: "Item 1"), level: 1, attributeValue: 1))
list.append(ListItem(text: NSAttributedString(string: "Item 2"), level: 1, attributeValue: 1))
// list.append(ListItem(text: NSAttributedString(string: "Item 2", attributes: [.font: UIFont.systemFont(ofSize: 18, weight: .bold)]), level: 1, attributeValue: 1))
list.append(ListItem(text: NSAttributedString(string: "Item 3"), level: 1, attributeValue: 2))
let text = ListParser.parse(list: list, indent: indent)
print(text)
The output in terms of structure is same if you use the commented line for "Item 2" or the uncommented one.
Are you able to provide a test that can help me understand the issue better?
Also, you may consider the following function in combination with what you are using which will still allow you to get the range of text for each of the list item, which you can possibly use to append list tags:
func parse(attributedString: NSAttributedString, indent: CGFloat = 25) -> [(range: NSRange, listItem: ListItem)]
Hi @rajdeep Thanks for getting back to me.
each new line is a start of a new paragraph
This is not the case from my tests though; if I create a list with no formatting on any of the list items, then the whole list, i.e. each list item, is sent in one paragraph block from the string.enumerateInlineContents().forEach
call, like this:
these
are
passed
in
one
paragraph {
NSColor = "<UIDynamicCatalogSystemColor: 0x600002b69000; name = labelColor>";
NSFont = "<UICTFont: 0x7f809e1a8530> font-family: \".SFUI-Regular\"; font-weight: normal; font-style: normal; font-size: 17.00pt";
NSParagraphStyle = "Alignment 4, LineSpacing 0, ParagraphSpacing 0, ParagraphSpacingBefore 0, HeadIndent 25, TailIndent 0, FirstLineHeadIndent 25, LineHeight 0/0, LineHeightMultiple 0, LineBreakMode 0, Tabs (\n 28L,\n 56L,\n 84L,\n 112L,\n 140L,\n 168L,\n 196L,\n 224L,\n 252L,\n 280L,\n 308L,\n 336L\n), DefaultTabInterval 0, Blocks (\n), Lists (\n), BaseWritingDirection 0, HyphenationFactor 0, TighteningForTruncation NO, HeaderLevel 0 LineBreakStrategy 0 PresentationIntents (\n) ListIntentOrdinal 0 CodeBlockIntentLanguageHint ''";
"_listItem" = "TextEditorFormat.unorderedList";
}), enclosingRange: Optional({0, 35}))
When I parse the paragraph with ListParser
then yes, each item is extracted one at a time. This way I can easily create an unordered list in HTML, because if I know the paragraph is a list I can create opening and closing tags:
<ul>
ParseList -> Create an li for each list item
</ul>
When there is formatting on the list items then I don't get the whole list in one paragraph, i.e. in the string.enumerateInlineContents().forEach
block each line item is sent as a new paragraph which makes it impossible to know when to open and close the list with HTML tags. For example:
these
{
NSColor = "<UIDynamicCatalogSystemColor: 0x600002b69000; name = labelColor>";
NSFont = "<UICTFont: 0x7f809e1a8530> font-family: \".SFUI-Regular\"; font-weight: normal; font-style: normal; font-size: 17.00pt";
NSParagraphStyle = "Alignment 4, LineSpacing 0, ParagraphSpacing 0, ParagraphSpacingBefore 0, HeadIndent 25, TailIndent 0, FirstLineHeadIndent 25, LineHeight 0/0, LineHeightMultiple 0, LineBreakMode 0, Tabs (\n 28L,\n 56L,\n 84L,\n 112L,\n 140L,\n 168L,\n 196L,\n 224L,\n 252L,\n 280L,\n 308L,\n 336L\n), DefaultTabInterval 0, Blocks (\n), Lists (\n), BaseWritingDirection 0, HyphenationFactor 0, TighteningForTruncation NO, HeaderLevel 0 LineBreakStrategy 0 PresentationIntents (\n) ListIntentOrdinal 0 CodeBlockIntentLanguageHint ''";
"_listItem" = "TextEditorFormat.unorderedList";
}), enclosingRange: Optional({0, 7}))
are{
NSColor = "<UIDynamicCatalogSystemColor: 0x600002b69000; name = labelColor>";
NSFont = "<UICTFont: 0x7f809e19e350> font-family: \".SFUI-SemiboldItalic\"; font-weight: bold; font-style: italic; font-size: 17.00pt";
NSParagraphStyle = "Alignment 4, LineSpacing 0, ParagraphSpacing 0, ParagraphSpacingBefore 0, HeadIndent 25, TailIndent 0, FirstLineHeadIndent 25, LineHeight 0/0, LineHeightMultiple 0, LineBreakMode 0, Tabs (\n 28L,\n 56L,\n 84L,\n 112L,\n 140L,\n 168L,\n 196L,\n 224L,\n 252L,\n 280L,\n 308L,\n 336L\n), DefaultTabInterval 0, Blocks (\n), Lists (\n), BaseWritingDirection 0, HyphenationFactor 0, TighteningForTruncation NO, HeaderLevel 0 LineBreakStrategy 0 PresentationIntents (\n) ListIntentOrdinal 0 CodeBlockIntentLanguageHint ''";
"_listItem" = "TextEditorFormat.unorderedList";
}), enclosingRange: Optional({7, 3}))
passed{
NSColor = "<UIDynamicCatalogSystemColor: 0x600002b69000; name = labelColor>";
NSFont = "<UICTFont: 0x7f809d7ca790> font-family: \".SFUI-RegularItalic\"; font-weight: normal; font-style: italic; font-size: 17.00pt";
NSParagraphStyle = "Alignment 4, LineSpacing 0, ParagraphSpacing 0, ParagraphSpacingBefore 0, HeadIndent 25, TailIndent 0, FirstLineHeadIndent 25, LineHeight 0/0, LineHeightMultiple 0, LineBreakMode 0, Tabs (\n 28L,\n 56L,\n 84L,\n 112L,\n 140L,\n 168L,\n 196L,\n 224L,\n 252L,\n 280L,\n 308L,\n 336L\n), DefaultTabInterval 0, Blocks (\n), Lists (\n), BaseWritingDirection 0, HyphenationFactor 0, TighteningForTruncation NO, HeaderLevel 0 LineBreakStrategy 0 PresentationIntents (\n) ListIntentOrdinal 0 CodeBlockIntentLanguageHint ''";
"_listItem" = "TextEditorFormat.unorderedList";
}), enclosingRange: Optional({11, 6}))
etc.
Let me know if you need any more information and I'll try and put a test project together. Parsing the list isn't a problem, but knowing when to insert the opening and closing list tags is.
@danielallsopp, this is by design in case of enumerateInlineContents
. How I see this being used is to get fine grain information of the contents, including text, for something like persistence to JSON or similar.
If you notice, in the example you shared, the font
for each line is different, even though paragraphStyle
is the same:
these
{
...
NSFont = "<UICTFont: 0x7f809e1a8530> **font-family: \".SFUI-Regular\";** font-weight: normal; font-style: normal; font-size: 17.00pt";
...
are{
...
NSFont = "<UICTFont: 0x7f809e19e350> font-family: \".SFUI-SemiboldItalic\"; font-weight: bold; font-style: italic; font-size: 17.00pt";
...
passed{
...
NSFont = "<UICTFont: 0x7f809d7ca790> font-family: \".SFUI-RegularItalic\"; font-weight: normal; font-style: italic; font-size: 17.00pt";
...
etc.
If you would like to get info individually, you may try one of the following:
attributedString
and drop attributes like font
that may be different and then, read the text back.attributedString.enumerateAttribute
on .paragraphStyle
without specifying option for longestEffectiveRangeNotRequired
. It's because of this option that the string gets broken down when any of the attribute in given range is different.Also, for your purposes, can you not just use the contents parsed by ListParser
and take the ranges from that to append the list start and end tags?
Hope this helps.
If you feel I am still missing the context, I think best would be to provide a failing unit test that I can look at to understand your requirements. 🙂
Thanks for your suggestions @rajdeep, I'm going to go through my code now and see if I can use them to get around my issue. I will get back to you as soon as possible with my findings.
please feel to reopen this issue if any further discussion is required.
I'm currently writing an HTML exporter for Proton, using the JSON examples and the
ListParserTests
as a guide. Everything works great if the text in any of the list items doesn't contain any formatting, such as bold or italic:The
ListParser
will return an array containing 3ListItem
values, i.e. theenumerateInlineContents().forEach { content in }
block will contain the entire list in thecontent
variable, i.e. I can add the opening tag<ul>
, enumerate through theListItems
adding each one to my encoded string, and then finally close the list with</ul>
. The encoded string looks like this:Encoded String: '["<ul><li>one</li><li>two</li><li>three</li></ul>"]'
However, if I format the text in the list, like this:
Then the
ListParser
will return a new paragraph for each formatted item, which is making it difficult for me to differentiate when a list should be opened and closed so my encoded string ends up like this:Encoded String: '["<ul><li><b>one</b></li></ul><ul><li><i>two</i></li></ul><ul><li><i><b>three</b></i></li></ul>"]'
Any ideas, or is this a known issue, or do I need to rethink my strategy for encoding lists?