@Nullable @Override public Object getSpans(@NonNull SpannableConfiguration configuration, @NonNull HtmlTag tag) { final Map<String, String> attributes = tag.attributes(); final String src = attributes.get("src"); if (TextUtils.isEmpty(src)) { return null; } final String destination = configuration.urlProcessor().process(src); // todo: replacement text is link... as we are not at block level // and cannot inspect the parent of this node... (img and a are both inlines) // // but we can look and see if we are inside a LinkSpan (will have to extend TagHandler // to obtain an instance SpannableBuilder for inspection) return configuration.factory().image( configuration.theme(), destination, configuration.asyncDrawableLoader(), configuration.imageSizeResolver(), imageSizeParser.parse(tag.attributes()), false ); } }
@Override public void handle( @NonNull SpannableConfiguration configuration, @NonNull SpannableBuilder builder, @NonNull HtmlTag tag) { if (tag.isBlock()) { visitChildren(configuration, builder, tag.getAsBlock()); } SpannableBuilder.setSpans( builder, configuration.factory().blockQuote(configuration.theme()), tag.start(), tag.end() ); } }
@Nullable @Override public Object getSpans(@NonNull SpannableConfiguration configuration, @NonNull HtmlTag tag) { return configuration.factory().emphasis(); } }
@Override public void visit(Code code) { final int length = builder.length(); // NB, in order to provide a _padding_ feeling code is wrapped inside two unbreakable spaces // unfortunately we cannot use this for multiline code as we cannot control where a new line break will be inserted builder.append('\u00a0'); builder.append(code.getLiteral()); builder.append('\u00a0'); setSpan(length, factory.code(theme, false)); }
spans = configuration.factory().orderedListItem( configuration.theme(), number++ ); } else { spans = configuration.factory().bulletListItem( configuration.theme(), bulletLevel
@Override public void visit(CustomNode customNode) { if (customNode instanceof Strikethrough) { final int length = builder.length(); visitChildren(customNode); setSpan(length, factory.strikethrough()); } else if (customNode instanceof TaskListItem) { // new in 1.0.1 final TaskListItem listItem = (TaskListItem) customNode; final int length = builder.length(); blockIndent += listItem.indent(); visitChildren(customNode); setSpan(length, factory.taskListItem(theme, blockIndent, listItem.done())); if (hasNext(customNode)) { newLine(); } blockIndent -= listItem.indent(); } else if (!handleTableNodes(customNode)) { super.visit(customNode); } }
@Nullable @Override public Object getSpans(@NonNull SpannableConfiguration configuration, @NonNull HtmlTag tag) { final String destination = tag.attributes().get("href"); if (!TextUtils.isEmpty(destination)) { return configuration.factory().link( configuration.theme(), configuration.urlProcessor().process(destination), configuration.linkResolver() ); } return null; } }
@Nullable @Override public Object getSpans(@NonNull SpannableConfiguration configuration, @NonNull HtmlTag tag) { return configuration.factory().heading(configuration.theme(), level); } }
@Override public void handle( @NonNull SpannableConfiguration configuration, @NonNull SpannableBuilder builder, @NonNull HtmlTag tag) { if (tag.isBlock()) { visitChildren(configuration, builder, tag.getAsBlock()); } SpannableBuilder.setSpans( builder, configuration.factory().strikethrough(), tag.start(), tag.end() ); } }
@Override public void visit(Paragraph paragraph) { final boolean inTightList = isInTightList(paragraph); if (!inTightList) { newLine(); } final int length = builder.length(); visitChildren(paragraph); // @since 1.1.1 apply paragraph span setSpan(length, factory.paragraph(inTightList)); if (hasNext(paragraph) && !inTightList) { newLine(); forceNewLine(); } }
/** * @param info tag of a code block * @param code content of a code block * @since 1.0.4 */ private void visitCodeBlock(@Nullable String info, @NonNull String code, @NonNull Node node) { newLine(); final int length = builder.length(); // empty lines on top & bottom builder.append('\u00a0').append('\n'); builder.append( configuration.syntaxHighlight() .highlight(info, code) ); newLine(); builder.append('\u00a0'); setSpan(length, factory.code(theme, true)); if (hasNext(node)) { newLine(); forceNewLine(); } }
@Override public void visit(ListItem listItem) { final int length = builder.length(); blockIndent += 1; listLevel += 1; final Node parent = listItem.getParent(); if (parent instanceof OrderedList) { final int start = ((OrderedList) parent).getStartNumber(); visitChildren(listItem); setSpan(length, factory.orderedListItem(theme, start)); // after we have visited the children increment start number final OrderedList orderedList = (OrderedList) parent; orderedList.setStartNumber(orderedList.getStartNumber() + 1); } else { visitChildren(listItem); setSpan(length, factory.bulletListItem(theme, listLevel - 1)); } blockIndent -= 1; listLevel -= 1; if (hasNext(listItem)) { newLine(); } }
@Override public void visit(Link link) { final int length = builder.length(); visitChildren(link); final String destination = configuration.urlProcessor().process(link.getDestination()); setSpan(length, factory.link(theme, destination, configuration.linkResolver())); }
@Override public void visit(Heading heading) { newLine(); final int length = builder.length(); visitChildren(heading); setSpan(length, factory.heading(theme, heading.getLevel())); if (hasNext(heading)) { newLine(); // after heading we add another line anyway (no additional checks) forceNewLine(); } }
when(factory.code(any(SpannableTheme.class), anyBoolean())).thenReturn(codeSpan);
@Override public void visit(Image image) { final int length = builder.length(); visitChildren(image); // we must check if anything _was_ added, as we need at least one char to render if (length == builder.length()) { builder.append('\uFFFC'); } final Node parent = image.getParent(); final boolean link = parent != null && parent instanceof Link; final String destination = configuration.urlProcessor().process(image.getDestination()); setSpan( length, factory.image( theme, destination, configuration.asyncDrawableLoader(), configuration.imageSizeResolver(), null, link ) ); // todo, maybe, if image is not inside a link, we should make it clickable, so // user can open it in external viewer? }
@Override public void visit(Emphasis emphasis) { final int length = builder.length(); visitChildren(emphasis); setSpan(length, factory.emphasis()); }
@Override public void visit(BlockQuote blockQuote) { newLine(); final int length = builder.length(); blockIndent += 1; visitChildren(blockQuote); setSpan(length, factory.blockQuote(theme)); blockIndent -= 1; if (hasNext(blockQuote)) { newLine(); forceNewLine(); } }
@Override public void visit(CustomBlock customBlock) { if (!(customBlock instanceof JLatexMathBlock)) { super.visit(customBlock); return; } final String latex = ((JLatexMathBlock) customBlock).latex(); final int length = builder.length(); builder.append(latex); SpannableBuilder.setSpans( builder, configuration.factory().image( configuration.theme(), JLatexMathMedia.makeDestination(latex), configuration.asyncDrawableLoader(), configuration.imageSizeResolver(), new ImageSize(new ImageSize.Dimension(100, "%"), null), false ), length, builder.length() ); } };