package gutenberg.itext.pegdown;
import com.google.common.collect.Maps;
import com.itextpdf.text.*;
import gutenberg.itext.*;
import gutenberg.pegdown.References;
import gutenberg.pegdown.TreeNavigation;
import gutenberg.pegdown.plugin.AttributesNode;
import gutenberg.pegdown.plugin.GenericBoxNode;
import gutenberg.util.Attributes;
import gutenberg.util.Collector;
import gutenberg.util.VariableResolver;
import org.pegdown.ast.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.Stack;
import static java.util.Arrays.fill;
/**
* @author <a href="http://twitter.com/aloyer">@aloyer</a>
*/
public class InvocationContext {
private final ITextContext iTextContext;
private final Styles styles;
private Logger log = LoggerFactory.getLogger(InvocationContext.class);
private final Processor processorDefault;
private final Stack<Font> fontStack;
private final Stack<TableInfos> tableStack;
private final FontAwesomeAdapter fontAwesome;
private final Stack<CellStyler> cellStylerStack;
private final TreeNavigation treeNavigation;
// ---
private Map<Class<?>, Processor> processors;
private Attributes[] attributesSeq = new Attributes[20];
private References references;
public InvocationContext(ITextContext iTextContext) throws IOException, DocumentException {
this.iTextContext = iTextContext;
this.styles = iTextContext.styles();
// ---
this.fontAwesome = new FontAwesomeAdapter();
this.processorDefault = new DefaultProcessor();
this.fontStack = new Stack<Font>();
this.fontStack.push(styles.defaultFont());
this.tableStack = new Stack<TableInfos>();
this.cellStylerStack = new Stack<CellStyler>();
this.treeNavigation = new TreeNavigation();
this.references = new References();
}
public ITextContext iTextContext() {
return iTextContext;
}
public References references() {
return references;
}
public VariableResolver variableResolver() {
return iTextContext().variableResolver();
}
public Chunk symbol(String symbol, float size, BaseColor color) {
return fontAwesome.symbol(symbol, size, color);
}
public void process(int depth, Node node) {
if (depth == 0) {
references.traverse(node);
}
Processor processor = processors().get(node.getClass());
if (processor == null) {
log.warn("No processor defined for type {}", node.getClass());
processor = processorDefault;
}
treeNavigation.push(node);
dumpProcessor(depth, node, processor);
processor.process(depth, node, this);
treeNavigation.pop();
}
private Map<Class<?>, Processor> processors() {
if (processors == null) {
processors = Maps.newHashMap();
initProcessors(iTextContext, styles);
}
return processors;
}
public TreeNavigation treeNavigation() {
return treeNavigation;
}
private void dumpProcessor(int depth, Node node, Processor processor) {
if (log.isDebugEnabled()) {
String indent = indent(depth);
StringBuilder out = new StringBuilder();
out.append(indent);
if (processor == processorDefault) {
out.append(" ");
} else {
out.append("*");
}
out.append(node);
if (node instanceof HeaderNode) {
out.append(" L:").append(((HeaderNode) node).getLevel());
}
if (node instanceof VerbatimNode) {
out.append(" T:").append(((VerbatimNode) node).getType());
}
if (node instanceof RefImageNode) {
RefImageNode rn = (RefImageNode) node;
out.append(" separatorSpace: '").append(rn.separatorSpace).append("' refKey: '").append(rn.referenceKey).append("'");
}
log.debug(out.toString());
}
}
private static String indent(int level) {
StringBuilder b = new StringBuilder();
for (int i = 0; i < level; i++)
b.append(" ");
return b.toString();
}
public void processChildren(int level, Node node) {
for (Node child : node.getChildren()) {
process(level + 1, child);
}
}
public List<Element> collectChildren(int level, Node node) {
Collector<Element> collector = new Collector<Element>();
iTextContext().pushElementConsumer(collector);
for (Node child : node.getChildren()) {
process(level + 1, child);
}
iTextContext().popElementConsumer();
return collector.getCollected();
}
private void applyAttributes(Element e, Attributes attributes) {
String align = attributes.getString("align");
if ("center".equalsIgnoreCase(align)) {
if (e instanceof Image) {
((Image) e).setAlignment(Image.MIDDLE);
} else if (e instanceof Paragraph) {
((Paragraph) e).setAlignment(Element.ALIGN_CENTER);
}
}
}
protected void initProcessors(ITextContext iTextContext, final Styles styles) {
processors.put(RootNode.class, new DefaultProcessor());
processors.put(AnchorLinkNode.class, new AnchorLinkNodeProcessor());
processors.put(SimpleNode.class, new SimpleNodeProcessor());
processors.put(BlockQuoteNode.class, new BlockQuoteNodeProcessor(styles));
processors.put(ParaNode.class, new ParaNodeProcessor());
processors.put(VerbatimNode.class, new VerbatimNodeProcessor());
processors.put(TextNode.class, new TextNodeProcessor());
processors.put(SpecialTextNode.class, new SpecialTextNodeProcessor());
processors.put(OrderedListNode.class, new OrderedListNodeProcessor());
processors.put(BulletListNode.class, new BulletListNodeProcessor());
processors.put(ListItemNode.class, new ListItemNodeProcessor());
processors.put(HeaderNode.class, new HeaderNodeProcessor());
processors.put(CodeNode.class, new CodeNodeProcessor(styles));
processors.put(StrongEmphSuperNode.class, new StrongEmphSuperNodeProcessor());
processors.put(StrikeNode.class, new StrikeNodeProcessor());
processors.put(SuperNode.class, new SuperNodeProcessor());
processors.put(TableNode.class, new TableNodeProcessor(new AlternateTableRowBackground(styles)));
processors.put(TableHeaderNode.class, new TableHeaderNodeProcessor(new DefaultHeaderCellStyler(styles)));
processors.put(TableBodyNode.class, new TableBodyNodeProcessor(new DefaultBodyCellStyler(styles)));
processors.put(TableRowNode.class, new TableRowNodeProcessor());
processors.put(TableCellNode.class, new TableCellNodeProcessor());
processors.put(ExpImageNode.class, new ExpImageNodeProcessor());
processors.put(ExpLinkNode.class, new ExpLinkNodeProcessor());
processors.put(RefImageNode.class, new RefImageNodeProcessor());
processors.put(RefLinkNode.class, new RefLinkNodeProcessor());
processors.put(AttributesNode.class, new AttributesNodeProcessor());
processors.put(GenericBoxNode.class, new GenericBoxNodeProcessor());
}
public Font peekFont() {
return fontStack.peek();
}
public Font peekSymbolFont() {
return ITextUtils.adjustWithStyles(styles.getSymbolFont(), peekFont());
}
public void pushFont(Font font) {
fontStack.push(font);
}
public Font popFont() {
return fontStack.pop();
}
public TableInfos peekTable() {
return tableStack.peek();
}
public void pushTable(TableInfos table) {
tableStack.push(table);
}
public TableInfos popTable() {
return tableStack.pop();
}
public CellStyler peekCellStyler() {
return cellStylerStack.peek();
}
public void pushCellStyler(CellStyler cellStyler) {
cellStylerStack.push(cellStyler);
}
public CellStyler popCellStyler() {
return cellStylerStack.pop();
}
public void pushAttributes(int level, Attributes attributes) {
if (level >= attributesSeq.length) {
Attributes[] increased = new Attributes[level + 5];
System.arraycopy(attributesSeq, 0, increased, 0, attributesSeq.length);
attributesSeq = increased;
}
fill(attributesSeq, level, attributesSeq.length, null);
attributesSeq[level] = attributes;
log.debug(indent(level) + "Attributes pushed for level {}; attributes: {}", level, attributes);
}
@SuppressWarnings("unchecked")
public Attributes peekAttributes(int level) {
return peekAttributes(level, false);
}
public Attributes peekAttributes(int level, boolean lookupForAncestor) {
if (level >= attributesSeq.length || attributesSeq[level] == null) {
if (lookupForAncestor) {
for (int i = level; i >= 0; i--) {
if (attributesSeq[i] != null)
return attributesSeq[i];
}
}
// still there, nothing found in ancestor tree
return new Attributes();
}
return attributesSeq[level];
}
public void append(Element element) {
if (element instanceof Chapter) {
flushPendingChapter();
Chapter pendingChapter = ((Chapter) element);
iTextContext().definePendingChapter(pendingChapter);
} else if (element instanceof Section) {
// nothing to do
} else {
iTextContext().append(element);
}
}
public void appendAll(Iterable<? extends Element> elements) {
for (Element e : elements)
append(e);
}
public void flushPendingChapter() {
iTextContext().flushPendingChapter();
}
public Chunk bulletSymbol() {
return iTextContext().styles().bulletSymbol();
}
}