18e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project/*
28e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project * Copyright (C) 2007 Alp Toker <alp@atoker.com>
38e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project * Copyright (C) 2007 Apple Inc.
48e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project *
58e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project * This library is free software; you can redistribute it and/or
68e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project * modify it under the terms of the GNU Library General Public
78e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project * License as published by the Free Software Foundation; either
88e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project * version 2 of the License, or (at your option) any later version.
98e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project *
108e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project * This library is distributed in the hope that it will be useful,
118e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project * but WITHOUT ANY WARRANTY; without even the implied warranty of
128e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
138e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project * Library General Public License for more details.
148e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project *
158e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project * You should have received a copy of the GNU Library General Public License
168e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project * along with this library; see the file COPYING.LIB.  If not, write to
178e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
188e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project * Boston, MA 02110-1301, USA.
198e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project */
208e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
218e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project#include "config.h"
228e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project#include "PrintContext.h"
238e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
248e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project#include "GraphicsContext.h"
258e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project#include "Frame.h"
268e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project#include "FrameView.h"
278e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project#include "RenderView.h"
288e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
298e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Projectusing namespace WebCore;
308e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
318e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Projectnamespace WebCore {
328e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
338e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source ProjectPrintContext::PrintContext(Frame* frame)
348e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    : m_frame(frame)
358e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project{
368e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project}
378e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
388e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source ProjectPrintContext::~PrintContext()
398e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project{
408e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    m_pageRects.clear();
418e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project}
428e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
438e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Projectint PrintContext::pageCount() const
448e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project{
458e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    return m_pageRects.size();
468e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project}
478e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
485e2bc6953fe6923165b8a5d7679939693a1d58d6Steve Blockconst IntRect& PrintContext::pageRect(int pageNumber) const
495e2bc6953fe6923165b8a5d7679939693a1d58d6Steve Block{
505e2bc6953fe6923165b8a5d7679939693a1d58d6Steve Block    return m_pageRects[pageNumber];
515e2bc6953fe6923165b8a5d7679939693a1d58d6Steve Block}
525e2bc6953fe6923165b8a5d7679939693a1d58d6Steve Block
538e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Projectvoid PrintContext::computePageRects(const FloatRect& printRect, float headerHeight, float footerHeight, float userScaleFactor, float& outPageHeight)
548e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project{
558e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    m_pageRects.clear();
568e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    outPageHeight = 0;
578e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
588e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    if (!m_frame->document() || !m_frame->view() || !m_frame->document()->renderer())
598e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project        return;
608e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
610bf48ef3be53ddaa52bbead65dfd75bf90e7a2b5Ben Murdoch    RenderView* root = toRenderView(m_frame->document()->renderer());
628e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
638e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    if (!root) {
648e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project        LOG_ERROR("document to be printed has no renderer");
658e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project        return;
668e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    }
678e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
688e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    float ratio = printRect.height() / printRect.width();
698e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
70231d4e3152a9c27a73b6ac7badbe6be673aa3ddfSteve Block    float pageWidth  = (float)root->rightLayoutOverflow();
718e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    float pageHeight = pageWidth * ratio;
728e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    outPageHeight = pageHeight;   // this is the height of the page adjusted by margins
738e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    pageHeight -= headerHeight + footerHeight;
748e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
758e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    if (pageHeight <= 0) {
768e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project        LOG_ERROR("pageHeight has bad value %.2f", pageHeight);
778e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project        return;
788e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    }
798e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
805e2bc6953fe6923165b8a5d7679939693a1d58d6Steve Block    computePageRectsWithPageSize(FloatSize(pageWidth, pageHeight), userScaleFactor);
815e2bc6953fe6923165b8a5d7679939693a1d58d6Steve Block}
825e2bc6953fe6923165b8a5d7679939693a1d58d6Steve Block
835e2bc6953fe6923165b8a5d7679939693a1d58d6Steve Blockvoid PrintContext::computePageRectsWithPageSize(const FloatSize& pageSizeInPixels, float userScaleFactor)
845e2bc6953fe6923165b8a5d7679939693a1d58d6Steve Block{
855e2bc6953fe6923165b8a5d7679939693a1d58d6Steve Block    RenderView* root = toRenderView(m_frame->document()->renderer());
865e2bc6953fe6923165b8a5d7679939693a1d58d6Steve Block
875e2bc6953fe6923165b8a5d7679939693a1d58d6Steve Block    if (!root) {
885e2bc6953fe6923165b8a5d7679939693a1d58d6Steve Block        LOG_ERROR("document to be printed has no renderer");
895e2bc6953fe6923165b8a5d7679939693a1d58d6Steve Block        return;
905e2bc6953fe6923165b8a5d7679939693a1d58d6Steve Block    }
915e2bc6953fe6923165b8a5d7679939693a1d58d6Steve Block
925e2bc6953fe6923165b8a5d7679939693a1d58d6Steve Block    if (userScaleFactor <= 0) {
935e2bc6953fe6923165b8a5d7679939693a1d58d6Steve Block        LOG_ERROR("userScaleFactor has bad value %.2f", userScaleFactor);
945e2bc6953fe6923165b8a5d7679939693a1d58d6Steve Block        return;
955e2bc6953fe6923165b8a5d7679939693a1d58d6Steve Block    }
965e2bc6953fe6923165b8a5d7679939693a1d58d6Steve Block
975e2bc6953fe6923165b8a5d7679939693a1d58d6Steve Block    float currPageHeight = pageSizeInPixels.height() / userScaleFactor;
988e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    float docHeight = root->layer()->height();
995e2bc6953fe6923165b8a5d7679939693a1d58d6Steve Block    float currPageWidth = pageSizeInPixels.width() / userScaleFactor;
1008e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
1018e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    // always return at least one page, since empty files should print a blank page
1025e2bc6953fe6923165b8a5d7679939693a1d58d6Steve Block    float printedPagesHeight = 0;
1038e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    do {
1045e2bc6953fe6923165b8a5d7679939693a1d58d6Steve Block        float proposedBottom = std::min(docHeight, printedPagesHeight + pageSizeInPixels.height());
1058f72e70a9fd78eec56623b3a62e68f16b7b27e28Feng Qian        m_frame->view()->adjustPageHeight(&proposedBottom, printedPagesHeight, proposedBottom, printedPagesHeight);
1068e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project        currPageHeight = max(1.0f, proposedBottom - printedPagesHeight);
1078e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
1088e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project        m_pageRects.append(IntRect(0, (int)printedPagesHeight, (int)currPageWidth, (int)currPageHeight));
1098e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project        printedPagesHeight += currPageHeight;
1108e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    } while (printedPagesHeight < docHeight);
1118e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project}
1128e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
1138e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Projectvoid PrintContext::begin(float width)
1148e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project{
115643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // By imaging to a width a little wider than the available pixels,
116643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // thin pages will be scaled down a little, matching the way they
117643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // print in IE and Camino. This lets them use fewer sheets than they
118643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // would otherwise, which is presumably why other browsers do this.
119643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // Wide pages will be scaled down more than this.
120643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    const float PrintingMinimumShrinkFactor = 1.25f;
121643ca7872b450ea4efacab6188849e5aac2ba161Steve Block
122643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // This number determines how small we are willing to reduce the page content
123643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // in order to accommodate the widest line. If the page would have to be
124643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // reduced smaller to make the widest line fit, we just clip instead (this
125643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    // behavior matches MacIE and Mozilla, at least)
126643ca7872b450ea4efacab6188849e5aac2ba161Steve Block    const float PrintingMaximumShrinkFactor = 2.0f;
1278e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
1288e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    float minLayoutWidth = width * PrintingMinimumShrinkFactor;
1298e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    float maxLayoutWidth = width * PrintingMaximumShrinkFactor;
1308e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
1318e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    // FIXME: This will modify the rendering of the on-screen frame.
1328e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    // Could lead to flicker during printing.
1338e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    m_frame->setPrinting(true, minLayoutWidth, maxLayoutWidth, true);
1348e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project}
1358e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
1368e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Projectvoid PrintContext::spoolPage(GraphicsContext& ctx, int pageNumber, float width)
1378e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project{
1388e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    IntRect pageRect = m_pageRects[pageNumber];
1398e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    float scale = width / pageRect.width();
1408e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
1418e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    ctx.save();
1428e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    ctx.scale(FloatSize(scale, scale));
1438e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    ctx.translate(-pageRect.x(), -pageRect.y());
1448e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    ctx.clip(pageRect);
1458e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    m_frame->view()->paintContents(&ctx, pageRect);
1468e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    ctx.restore();
1478e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project}
1488e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
1498e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Projectvoid PrintContext::end()
1508e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project{
1518e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project    m_frame->setPrinting(false, 0, 0, true);
1528e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project}
1538e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project
1545e2bc6953fe6923165b8a5d7679939693a1d58d6Steve Blockstatic RenderBoxModelObject* enclosingBoxModelObject(RenderObject* object)
1555e2bc6953fe6923165b8a5d7679939693a1d58d6Steve Block{
1565e2bc6953fe6923165b8a5d7679939693a1d58d6Steve Block
1575e2bc6953fe6923165b8a5d7679939693a1d58d6Steve Block    while (object && !object->isBoxModelObject())
1585e2bc6953fe6923165b8a5d7679939693a1d58d6Steve Block        object = object->parent();
1595e2bc6953fe6923165b8a5d7679939693a1d58d6Steve Block    if (!object)
1605e2bc6953fe6923165b8a5d7679939693a1d58d6Steve Block        return 0;
1615e2bc6953fe6923165b8a5d7679939693a1d58d6Steve Block    return toRenderBoxModelObject(object);
1625e2bc6953fe6923165b8a5d7679939693a1d58d6Steve Block}
1635e2bc6953fe6923165b8a5d7679939693a1d58d6Steve Block
1645e2bc6953fe6923165b8a5d7679939693a1d58d6Steve Blockint PrintContext::pageNumberForElement(Element* element, const FloatSize& pageSizeInPixels)
1655e2bc6953fe6923165b8a5d7679939693a1d58d6Steve Block{
1665e2bc6953fe6923165b8a5d7679939693a1d58d6Steve Block    // Make sure the element is not freed during the layout.
1675e2bc6953fe6923165b8a5d7679939693a1d58d6Steve Block    RefPtr<Element> elementRef(element);
1685e2bc6953fe6923165b8a5d7679939693a1d58d6Steve Block    element->document()->updateLayout();
1695e2bc6953fe6923165b8a5d7679939693a1d58d6Steve Block
1705e2bc6953fe6923165b8a5d7679939693a1d58d6Steve Block    RenderBoxModelObject* box = enclosingBoxModelObject(element->renderer());
1715e2bc6953fe6923165b8a5d7679939693a1d58d6Steve Block    if (!box)
1725e2bc6953fe6923165b8a5d7679939693a1d58d6Steve Block        return -1;
1735e2bc6953fe6923165b8a5d7679939693a1d58d6Steve Block
1745e2bc6953fe6923165b8a5d7679939693a1d58d6Steve Block    Frame* frame = element->document()->frame();
1755e2bc6953fe6923165b8a5d7679939693a1d58d6Steve Block    FloatRect pageRect(FloatPoint(0, 0), pageSizeInPixels);
1765e2bc6953fe6923165b8a5d7679939693a1d58d6Steve Block    PrintContext printContext(frame);
1775e2bc6953fe6923165b8a5d7679939693a1d58d6Steve Block    printContext.begin(pageRect.width());
1785e2bc6953fe6923165b8a5d7679939693a1d58d6Steve Block    printContext.computePageRectsWithPageSize(pageSizeInPixels, 1);
1795e2bc6953fe6923165b8a5d7679939693a1d58d6Steve Block
1805e2bc6953fe6923165b8a5d7679939693a1d58d6Steve Block    int top = box->offsetTop();
1815e2bc6953fe6923165b8a5d7679939693a1d58d6Steve Block    int left = box->offsetLeft();
1825e2bc6953fe6923165b8a5d7679939693a1d58d6Steve Block    for (int pageNumber = 0; pageNumber < printContext.pageCount(); pageNumber++) {
1835e2bc6953fe6923165b8a5d7679939693a1d58d6Steve Block        const IntRect& page = printContext.pageRect(pageNumber);
1845e2bc6953fe6923165b8a5d7679939693a1d58d6Steve Block        if (page.x() <= left && left < page.right() && page.y() <= top && top < page.bottom())
1855e2bc6953fe6923165b8a5d7679939693a1d58d6Steve Block            return pageNumber;
1865e2bc6953fe6923165b8a5d7679939693a1d58d6Steve Block    }
1878a0914b749bbe7da7768e07a7db5c6d4bb09472bSteve Block    printContext.end();
1885e2bc6953fe6923165b8a5d7679939693a1d58d6Steve Block    return -1;
1895e2bc6953fe6923165b8a5d7679939693a1d58d6Steve Block}
1905e2bc6953fe6923165b8a5d7679939693a1d58d6Steve Block
1918a0914b749bbe7da7768e07a7db5c6d4bb09472bSteve Blockint PrintContext::numberOfPages(Frame* frame, const FloatSize& pageSizeInPixels)
1928a0914b749bbe7da7768e07a7db5c6d4bb09472bSteve Block{
1938a0914b749bbe7da7768e07a7db5c6d4bb09472bSteve Block    frame->document()->updateLayout();
1948a0914b749bbe7da7768e07a7db5c6d4bb09472bSteve Block
1958a0914b749bbe7da7768e07a7db5c6d4bb09472bSteve Block    FloatRect pageRect(FloatPoint(0, 0), pageSizeInPixels);
1968a0914b749bbe7da7768e07a7db5c6d4bb09472bSteve Block    PrintContext printContext(frame);
1978a0914b749bbe7da7768e07a7db5c6d4bb09472bSteve Block    printContext.begin(pageRect.width());
1988a0914b749bbe7da7768e07a7db5c6d4bb09472bSteve Block    printContext.computePageRectsWithPageSize(pageSizeInPixels, 1);
1998a0914b749bbe7da7768e07a7db5c6d4bb09472bSteve Block    printContext.end();
2008a0914b749bbe7da7768e07a7db5c6d4bb09472bSteve Block    return printContext.pageCount();
2018a0914b749bbe7da7768e07a7db5c6d4bb09472bSteve Block}
2028a0914b749bbe7da7768e07a7db5c6d4bb09472bSteve Block
2038e35f3cfc7fba1d1c829dc557ebad6409cbe16a2The Android Open Source Project}
204