void PageRuleCollector::matchAllPageRules(int pageIndex)
{
    const bool isLeft = isLeftPage(pageIndex);
    const bool isFirst = isFirstPage(pageIndex);
    const String page = pageName(pageIndex);
    
    matchPageRules(CSSDefaultStyleSheets::defaultPrintStyle, isLeft, isFirst, page);
    matchPageRules(m_ruleSets.userStyle(), isLeft, isFirst, page);
    // Only consider the global author RuleSet for @page rules, as per the HTML5 spec.
    matchPageRules(m_ruleSets.authorStyle(), isLeft, isFirst, page);
}
PageRuleCollector::PageRuleCollector(const RenderStyle* rootElementStyle, int pageIndex)
    : m_isLeftPage(isLeftPage(rootElementStyle, pageIndex))
    , m_isFirstPage(isFirstPage(pageIndex))
    , m_pageName(pageName(pageIndex)) { }
PageRuleCollector::PageRuleCollector(const StyleResolverState& state, int pageIndex)
    : m_state(state)
    , m_isLeftPage(isLeftPage(pageIndex))
    , m_isFirstPage(isFirstPage(pageIndex))
    , m_pageName(pageName(pageIndex)) { }
SAWYER_EXPORT std::string
TextMarkup::finalizeDocument(const std::string &s_) {
    std::string s = boost::trim_copy(s_);

    // Remove pairs of "=back =over" and spaces at the ends of lines
    {
        boost::regex backOverRe("(^=back\\s*=over[ \t]*)|[ \\t\\r\\f]+$");
        std::ostringstream out(std::ios::out | std::ios::binary);
        std::ostream_iterator<char, char> oi(out);
        boost::regex_replace(oi, s.begin(), s.end(), backOverRe, "");
        s = out.str();
    }

    // Replace lots of blank lines with just one blank line
    {
        boost::regex blankLines("\\n{3,}");
        std::ostringstream out(std::ios::out | std::ios::binary);
        std::ostream_iterator<char, char> oi(out);
        boost::regex_replace(oi, s.begin(), s.end(), blankLines, "\n\n");
        s = out.str();
    }

    std::vector<std::string> lines;
    boost::split(lines, s, boost::is_any_of("\n"));

    // Indent and reflow the text since it tends to not have enough linefeeds.
    Markup::Reflow reflow(80);                          // standard tty width of 80 columns
    reflow.indentationString("    ");                   // four is good breathing room without looking stretched
    static const size_t itemFieldWidth = 3;             // one less than indentation
    std::vector<int> itemNumbers;                       // for numbered lists
    BOOST_FOREACH (const std::string &line, lines) {
        if (boost::starts_with(line, "=line")) {
            reflow.lineBreak();
        } else if (boost::starts_with(line, "=over")) {
            ++reflow;
            itemNumbers.push_back(0);
        } else if (boost::starts_with(line, "=back")) {
            --reflow;
            if (!itemNumbers.empty())
                itemNumbers.pop_back();
        } else if (boost::starts_with(line, "=item")) { // =item is an implied =over
            std::string style = boost::trim_copy(line.substr(5));
            if (style.empty()) {
                style = "*";
            } else if (isdigit(style[0]) && !itemNumbers.empty()) {
                style = boost::lexical_cast<std::string>(++itemNumbers.back()) + ".";
            }
            reflow(BaseMarkup::leftJustify(style, itemFieldWidth) + " ");
            ++reflow;
            itemNumbers.push_back(0);
        } else {
            reflow(line + "\n");
        }
    }
    s = reflow.toString();

    // Add the header and footer
    {
        std::string page = boost::to_upper_copy(pageName()) + "(" + chapterNumberOrDefault() + ")";
        std::string title = chapterTitleOrDefault();
        std::string version = versionStringOrDefault();
        std::string date = versionDateOrDefault();

        size_t headSize = page.size() + title.size() + page.size();
        size_t footSize = version.size() + date.size() + page.size();

        std::string headPad = reflow.pageWidth() > headSize + 2 ?
                              std::string(round(0.5 * (reflow.pageWidth() - (headSize+2))), ' ') :
                              std::string(" ");

        std::string footPad = reflow.pageWidth() > footSize + 2 ?
                              std::string(round(0.5 * (reflow.pageWidth() - (footSize+2))), ' ') :
                              std::string(" ");

        s = (doPageHeader_ ? page + headPad + title + headPad + page + "\n\n" : std::string()) +
            s +
            (doPageFooter_ ? "\n" + version + footPad + date + footPad + page + "\n" : std::string());
    }

    return s;
}