1 /* 2 * Copyright (C) 2007 Alp Toker <alp (at) atoker.com> 3 * Copyright (C) 2007 Apple Inc. 4 * 5 * This library is free software; you can redistribute it and/or 6 * modify it under the terms of the GNU Library General Public 7 * License as published by the Free Software Foundation; either 8 * version 2 of the License, or (at your option) any later version. 9 * 10 * This library is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 * Library General Public License for more details. 14 * 15 * You should have received a copy of the GNU Library General Public License 16 * along with this library; see the file COPYING.LIB. If not, write to 17 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 18 * Boston, MA 02110-1301, USA. 19 */ 20 21 #include "config.h" 22 #include "PrintContext.h" 23 24 #include "GraphicsContext.h" 25 #include "Frame.h" 26 #include "FrameView.h" 27 #include "RenderView.h" 28 29 using namespace WebCore; 30 31 namespace WebCore { 32 33 PrintContext::PrintContext(Frame* frame) 34 : m_frame(frame) 35 { 36 } 37 38 PrintContext::~PrintContext() 39 { 40 m_pageRects.clear(); 41 } 42 43 int PrintContext::pageCount() const 44 { 45 return m_pageRects.size(); 46 } 47 48 const IntRect& PrintContext::pageRect(int pageNumber) const 49 { 50 return m_pageRects[pageNumber]; 51 } 52 53 void PrintContext::computePageRects(const FloatRect& printRect, float headerHeight, float footerHeight, float userScaleFactor, float& outPageHeight) 54 { 55 m_pageRects.clear(); 56 outPageHeight = 0; 57 58 if (!m_frame->document() || !m_frame->view() || !m_frame->document()->renderer()) 59 return; 60 61 RenderView* root = toRenderView(m_frame->document()->renderer()); 62 63 if (!root) { 64 LOG_ERROR("document to be printed has no renderer"); 65 return; 66 } 67 68 float ratio = printRect.height() / printRect.width(); 69 70 float pageWidth = (float)root->rightLayoutOverflow(); 71 float pageHeight = pageWidth * ratio; 72 outPageHeight = pageHeight; // this is the height of the page adjusted by margins 73 pageHeight -= headerHeight + footerHeight; 74 75 if (pageHeight <= 0) { 76 LOG_ERROR("pageHeight has bad value %.2f", pageHeight); 77 return; 78 } 79 80 computePageRectsWithPageSize(FloatSize(pageWidth, pageHeight), userScaleFactor); 81 } 82 83 void PrintContext::computePageRectsWithPageSize(const FloatSize& pageSizeInPixels, float userScaleFactor) 84 { 85 RenderView* root = toRenderView(m_frame->document()->renderer()); 86 87 if (!root) { 88 LOG_ERROR("document to be printed has no renderer"); 89 return; 90 } 91 92 if (userScaleFactor <= 0) { 93 LOG_ERROR("userScaleFactor has bad value %.2f", userScaleFactor); 94 return; 95 } 96 97 float currPageHeight = pageSizeInPixels.height() / userScaleFactor; 98 float docHeight = root->layer()->height(); 99 float currPageWidth = pageSizeInPixels.width() / userScaleFactor; 100 101 // always return at least one page, since empty files should print a blank page 102 float printedPagesHeight = 0; 103 do { 104 float proposedBottom = std::min(docHeight, printedPagesHeight + pageSizeInPixels.height()); 105 m_frame->view()->adjustPageHeight(&proposedBottom, printedPagesHeight, proposedBottom, printedPagesHeight); 106 currPageHeight = max(1.0f, proposedBottom - printedPagesHeight); 107 108 m_pageRects.append(IntRect(0, (int)printedPagesHeight, (int)currPageWidth, (int)currPageHeight)); 109 printedPagesHeight += currPageHeight; 110 } while (printedPagesHeight < docHeight); 111 } 112 113 void PrintContext::begin(float width) 114 { 115 // By imaging to a width a little wider than the available pixels, 116 // thin pages will be scaled down a little, matching the way they 117 // print in IE and Camino. This lets them use fewer sheets than they 118 // would otherwise, which is presumably why other browsers do this. 119 // Wide pages will be scaled down more than this. 120 const float PrintingMinimumShrinkFactor = 1.25f; 121 122 // This number determines how small we are willing to reduce the page content 123 // in order to accommodate the widest line. If the page would have to be 124 // reduced smaller to make the widest line fit, we just clip instead (this 125 // behavior matches MacIE and Mozilla, at least) 126 const float PrintingMaximumShrinkFactor = 2.0f; 127 128 float minLayoutWidth = width * PrintingMinimumShrinkFactor; 129 float maxLayoutWidth = width * PrintingMaximumShrinkFactor; 130 131 // FIXME: This will modify the rendering of the on-screen frame. 132 // Could lead to flicker during printing. 133 m_frame->setPrinting(true, minLayoutWidth, maxLayoutWidth, true); 134 } 135 136 void PrintContext::spoolPage(GraphicsContext& ctx, int pageNumber, float width) 137 { 138 IntRect pageRect = m_pageRects[pageNumber]; 139 float scale = width / pageRect.width(); 140 141 ctx.save(); 142 ctx.scale(FloatSize(scale, scale)); 143 ctx.translate(-pageRect.x(), -pageRect.y()); 144 ctx.clip(pageRect); 145 m_frame->view()->paintContents(&ctx, pageRect); 146 ctx.restore(); 147 } 148 149 void PrintContext::end() 150 { 151 m_frame->setPrinting(false, 0, 0, true); 152 } 153 154 static RenderBoxModelObject* enclosingBoxModelObject(RenderObject* object) 155 { 156 157 while (object && !object->isBoxModelObject()) 158 object = object->parent(); 159 if (!object) 160 return 0; 161 return toRenderBoxModelObject(object); 162 } 163 164 int PrintContext::pageNumberForElement(Element* element, const FloatSize& pageSizeInPixels) 165 { 166 // Make sure the element is not freed during the layout. 167 RefPtr<Element> elementRef(element); 168 element->document()->updateLayout(); 169 170 RenderBoxModelObject* box = enclosingBoxModelObject(element->renderer()); 171 if (!box) 172 return -1; 173 174 Frame* frame = element->document()->frame(); 175 FloatRect pageRect(FloatPoint(0, 0), pageSizeInPixels); 176 PrintContext printContext(frame); 177 printContext.begin(pageRect.width()); 178 printContext.computePageRectsWithPageSize(pageSizeInPixels, 1); 179 180 int top = box->offsetTop(); 181 int left = box->offsetLeft(); 182 for (int pageNumber = 0; pageNumber < printContext.pageCount(); pageNumber++) { 183 const IntRect& page = printContext.pageRect(pageNumber); 184 if (page.x() <= left && left < page.right() && page.y() <= top && top < page.bottom()) 185 return pageNumber; 186 } 187 printContext.end(); 188 return -1; 189 } 190 191 int PrintContext::numberOfPages(Frame* frame, const FloatSize& pageSizeInPixels) 192 { 193 frame->document()->updateLayout(); 194 195 FloatRect pageRect(FloatPoint(0, 0), pageSizeInPixels); 196 PrintContext printContext(frame); 197 printContext.begin(pageRect.width()); 198 printContext.computePageRectsWithPageSize(pageSizeInPixels, 1); 199 printContext.end(); 200 return printContext.pageCount(); 201 } 202 203 } 204