diff --git a/rtf-group.js b/rtf-group.js index 46d83ce..acf0f12 100644 --- a/rtf-group.js +++ b/rtf-group.js @@ -9,18 +9,48 @@ class RTFGroup { this.style = {} this.ignorable = null } + // These walk up the group chain iteratively rather than by recursion. RTF + // groups nest via `{`, so the chain is as deep as the document nests it, and + // recursing once per lookup overflows the stack on deeply nested input. The + // walk stops at RTFDocument, which overrides each of these to return directly. get (name) { - return this[name] != null ? this[name] : this.parent.get(name) + for (let group = this; group != null; group = group.parent) { + if (group.parent == null) return group.get(name) + if (group[name] != null) return group[name] + } } getFont (num) { - return this.fonts[num] != null ? this.fonts[num] : this.parent.getFont(num) + for (let group = this; group != null; group = group.parent) { + if (group.parent == null) return group.getFont(num) + if (group.fonts[num] != null) return group.fonts[num] + } } getColor (num) { - return this.colors[num] != null ? this.colors[num] : this.parent.getFont(num) + for (let group = this; group != null; group = group.parent) { + if (group.parent == null) return group.getFont(num) + if (group.colors[num] != null) return group.colors[num] + } } getStyle (name) { - if (!name) return Object.assign({}, this.parent.getStyle(), this.style) - return this.style[name] != null ? this.style[name] : this.parent.getStyle(name) + if (!name) { + // Collect the chain, then merge root-first in a single pass. Recursing + // allocated a fresh object at every level for every lookup. + const chain = [] + let group = this + while (group.parent != null) { + chain.push(group) + group = group.parent + } + const style = Object.assign({}, group.getStyle()) + for (let i = chain.length - 1; i >= 0; i--) { + Object.assign(style, chain[i].style) + } + return style + } + for (let group = this; group != null; group = group.parent) { + if (group.parent == null) return group.getStyle(name) + if (group.style[name] != null) return group.style[name] + } } resetStyle () { this.style = {}