blob: 589085b0145c746dd99cb6bb456164553ea26126 [file] [log] [blame]
license.botbf09a502008-08-24 00:55:551// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
initial.commit09911bf2008-07-26 23:55:294
5#include "chrome/browser/printing/printed_document.h"
6
7#include <set>
8
initial.commit09911bf2008-07-26 23:55:299#include "base/message_loop.h"
10#include "base/time.h"
11#include "chrome/browser/printing/page_number.h"
12#include "chrome/browser/printing/page_overlays.h"
13#include "chrome/browser/printing/printed_pages_source.h"
14#include "chrome/browser/printing/printed_page.h"
initial.commit09911bf2008-07-26 23:55:2915#include "chrome/common/gfx/chrome_font.h"
16#include "chrome/common/gfx/emf.h"
[email protected]05283722008-11-06 21:27:3717#include "chrome/common/gfx/text_elider.h"
initial.commit09911bf2008-07-26 23:55:2918#include "chrome/common/time_format.h"
19#include "chrome/common/notification_service.h"
20#include "chrome/common/win_util.h"
[email protected]4ae30d082009-02-20 17:55:5521#include "printing/units.h"
[email protected]c399a8a2008-11-22 19:38:0022#include "skia/ext/platform_device.h"
initial.commit09911bf2008-07-26 23:55:2923
[email protected]e1acf6f2008-10-27 20:43:3324using base::Time;
25
initial.commit09911bf2008-07-26 23:55:2926namespace printing {
27
28PrintedDocument::PrintedDocument(const PrintSettings& settings,
29 PrintedPagesSource* source,
30 int cookie)
31 : mutable_(source),
32 immutable_(settings, source, cookie) {
33
34 // Records the expected page count if a range is setup.
35 if (!settings.ranges.empty()) {
36 // If there is a range, set the number of page
37 for (unsigned i = 0; i < settings.ranges.size(); ++i) {
38 const PageRange& range = settings.ranges[i];
39 mutable_.expected_page_count_ += range.to - range.from + 1;
40 }
41 }
42}
43
44PrintedDocument::~PrintedDocument() {
45}
46
47void PrintedDocument::SetPage(int page_number, gfx::Emf* emf, double shrink) {
48 // Notice the page_number + 1, the reason is that this is the value that will
49 // be shown. Users dislike 0-based counting.
50 scoped_refptr<PrintedPage> page(
51 new PrintedPage(page_number + 1,
52 emf, immutable_.settings_.page_setup_pixels().physical_size()));
53 {
54 AutoLock lock(lock_);
55 mutable_.pages_[page_number] = page;
56 if (mutable_.shrink_factor == 0) {
57 mutable_.shrink_factor = shrink;
58 } else {
59 DCHECK_EQ(mutable_.shrink_factor, shrink);
60 }
61 }
62 NotificationService::current()->Notify(
[email protected]bfd04a62009-02-01 18:16:5663 NotificationType::PRINTED_DOCUMENT_UPDATED,
initial.commit09911bf2008-07-26 23:55:2964 Source<PrintedDocument>(this),
65 Details<PrintedPage>(page));
66}
67
68bool PrintedDocument::GetPage(int page_number,
69 scoped_refptr<PrintedPage>* page) {
70 bool request = false;
71 {
72 AutoLock lock(lock_);
73 PrintedPages::const_iterator itr = mutable_.pages_.find(page_number);
74 if (itr != mutable_.pages_.end()) {
75 if (itr->second.get()) {
76 *page = itr->second;
77 return true;
78 }
79 request = false;
80 } else {
81 request = true;
82 // Force the creation to not repeatedly request the same page.
83 mutable_.pages_[page_number];
84 }
85 }
86 if (request) {
87 PrintPage_ThreadJump(page_number);
88 }
89 return false;
90}
91
92void PrintedDocument::RenderPrintedPage(const PrintedPage& page,
93 HDC context) const {
[email protected]0ae80b892008-10-15 17:56:4094#ifndef NDEBUG
initial.commit09911bf2008-07-26 23:55:2995 {
96 // Make sure the page is from our list.
97 AutoLock lock(lock_);
98 DCHECK(&page == mutable_.pages_.find(page.page_number() - 1)->second.get());
99 }
100#endif
101
102 // Save the state to make sure the context this function call does not modify
103 // the device context.
104 int saved_state = SaveDC(context);
105 DCHECK_NE(saved_state, 0);
[email protected]21f527e2008-12-17 23:29:40106 skia::PlatformDeviceWin::InitializeDC(context);
initial.commit09911bf2008-07-26 23:55:29107 {
108 // Save the state (again) to apply the necessary world transformation.
109 int saved_state = SaveDC(context);
110 DCHECK_NE(saved_state, 0);
111
112 // Setup the matrix to translate and scale to the right place. Take in
113 // account the actual shrinking factor.
114 XFORM xform = { 0 };
115 xform.eDx = static_cast<float>(
116 immutable_.settings_.page_setup_pixels().content_area().x());
117 xform.eDy = static_cast<float>(
118 immutable_.settings_.page_setup_pixels().content_area().y());
119 xform.eM11 = static_cast<float>(1. / mutable_.shrink_factor);
120 xform.eM22 = static_cast<float>(1. / mutable_.shrink_factor);
121 BOOL res = ModifyWorldTransform(context, &xform, MWT_LEFTMULTIPLY);
122 DCHECK_NE(res, 0);
123
124 if (!page.emf()->SafePlayback(context)) {
125 NOTREACHED();
126 }
127
128 res = RestoreDC(context, saved_state);
129 DCHECK_NE(res, 0);
130 }
131
132 // Print the header and footer.
133 int base_font_size = ChromeFont().height();
[email protected]74d9bac2008-09-11 21:25:07134 int new_font_size = ConvertUnit(10,
135 immutable_.settings_.desired_dpi,
136 immutable_.settings_.dpi());
initial.commit09911bf2008-07-26 23:55:29137 DCHECK_GT(new_font_size, base_font_size);
138 ChromeFont font(ChromeFont().DeriveFont(new_font_size - base_font_size));
139 HGDIOBJ old_font = SelectObject(context, font.hfont());
140 DCHECK(old_font != NULL);
141 // We don't want a white square around the text ever if overflowing.
142 SetBkMode(context, TRANSPARENT);
143 PrintHeaderFooter(context, page, PageOverlays::LEFT, PageOverlays::TOP,
144 font);
145 PrintHeaderFooter(context, page, PageOverlays::CENTER, PageOverlays::TOP,
146 font);
147 PrintHeaderFooter(context, page, PageOverlays::RIGHT, PageOverlays::TOP,
148 font);
149 PrintHeaderFooter(context, page, PageOverlays::LEFT, PageOverlays::BOTTOM,
150 font);
151 PrintHeaderFooter(context, page, PageOverlays::CENTER, PageOverlays::BOTTOM,
152 font);
153 PrintHeaderFooter(context, page, PageOverlays::RIGHT, PageOverlays::BOTTOM,
154 font);
155 int res = RestoreDC(context, saved_state);
156 DCHECK_NE(res, 0);
157}
158
159bool PrintedDocument::RenderPrintedPageNumber(int page_number, HDC context) {
160 scoped_refptr<PrintedPage> page;
161 if (!GetPage(page_number, &page))
162 return false;
163 RenderPrintedPage(*page.get(), context);
164 return true;
165}
166
167bool PrintedDocument::IsComplete() const {
168 AutoLock lock(lock_);
169 if (!mutable_.page_count_)
170 return false;
171 PageNumber page(immutable_.settings_, mutable_.page_count_);
172 if (page == PageNumber::npos())
173 return false;
174 for (; page != PageNumber::npos(); ++page) {
175 PrintedPages::const_iterator itr = mutable_.pages_.find(page.ToInt());
176 if (itr == mutable_.pages_.end() || !itr->second.get() ||
177 !itr->second->emf())
178 return false;
179 }
180 return true;
181}
182
183bool PrintedDocument::RequestMissingPages() {
184 typedef std::set<int> PageNumbers;
185 PageNumbers missing_pages;
186 {
187 AutoLock lock(lock_);
188 PageNumber page(immutable_.settings_, mutable_.page_count_);
189 if (page == PageNumber::npos())
190 return false;
191 for (; page != PageNumber::npos(); ++page) {
192 PrintedPage* printed_page = mutable_.pages_[page.ToInt()].get();
193 if (!printed_page || !printed_page->emf())
194 missing_pages.insert(page.ToInt());
195 }
196 }
197 if (!missing_pages.size())
198 return true;
199 PageNumbers::const_iterator end = missing_pages.end();
200 for (PageNumbers::const_iterator itr = missing_pages.begin();
201 itr != end;
202 ++itr) {
203 int page_number = *itr;
204 PrintPage_ThreadJump(page_number);
205 }
206 return true;
207}
208
209void PrintedDocument::DisconnectSource() {
210 AutoLock lock(lock_);
211 mutable_.source_ = NULL;
212}
213
214size_t PrintedDocument::MemoryUsage() const {
215 std::vector<scoped_refptr<PrintedPage>> pages_copy;
216 {
217 AutoLock lock(lock_);
218 pages_copy.reserve(mutable_.pages_.size());
219 PrintedPages::const_iterator end = mutable_.pages_.end();
220 for (PrintedPages::const_iterator itr = mutable_.pages_.begin();
221 itr != end; ++itr) {
222 if (itr->second.get()) {
223 pages_copy.push_back(itr->second);
224 }
225 }
226 }
227 size_t total = 0;
228 for (size_t i = 0; i < pages_copy.size(); ++i) {
229 total += pages_copy[i]->emf()->GetDataSize();
230 }
231 return total;
232}
233
234void PrintedDocument::set_page_count(int max_page) {
235 {
236 AutoLock lock(lock_);
237 DCHECK_EQ(0, mutable_.page_count_);
238 mutable_.page_count_ = max_page;
239 if (immutable_.settings_.ranges.empty()) {
240 mutable_.expected_page_count_ = max_page;
241 } else {
242 // If there is a range, don't bother since expected_page_count_ is already
243 // initialized.
244 DCHECK_NE(mutable_.expected_page_count_, 0);
245 }
246 }
247 NotificationService::current()->Notify(
[email protected]bfd04a62009-02-01 18:16:56248 NotificationType::PRINTED_DOCUMENT_UPDATED,
initial.commit09911bf2008-07-26 23:55:29249 Source<PrintedDocument>(this),
250 NotificationService::NoDetails());
251}
252
253int PrintedDocument::page_count() const {
254 AutoLock lock(lock_);
255 return mutable_.page_count_;
256}
257
258int PrintedDocument::expected_page_count() const {
259 AutoLock lock(lock_);
260 return mutable_.expected_page_count_;
261}
262
263void PrintedDocument::PrintHeaderFooter(HDC context,
264 const PrintedPage& page,
265 PageOverlays::HorizontalPosition x,
266 PageOverlays::VerticalPosition y,
267 const ChromeFont& font) const {
268 const PrintSettings& settings = immutable_.settings_;
269 const std::wstring& line = settings.overlays.GetOverlay(x, y);
270 if (line.empty()) {
271 return;
272 }
273 std::wstring output(PageOverlays::ReplaceVariables(line, *this, page));
274 if (output.empty()) {
275 // May happens if document name or url is empty.
276 return;
277 }
278 const gfx::Size string_size(font.GetStringWidth(output), font.height());
279 gfx::Rect bounding;
280 bounding.set_height(string_size.height());
281 const gfx::Rect& overlay_area(settings.page_setup_pixels().overlay_area());
282 // Hard code .25 cm interstice between overlays. Make sure that some space is
283 // kept between each headers.
284 const int interstice = ConvertUnit(250, kHundrethsMMPerInch, settings.dpi());
285 const int max_width = overlay_area.width() / 3 - interstice;
286 const int actual_width = std::min(string_size.width(), max_width);
287 switch (x) {
288 case PageOverlays::LEFT:
289 bounding.set_x(overlay_area.x());
290 bounding.set_width(max_width);
291 break;
292 case PageOverlays::CENTER:
293 bounding.set_x(overlay_area.x() +
294 (overlay_area.width() - actual_width) / 2);
295 bounding.set_width(actual_width);
296 break;
297 case PageOverlays::RIGHT:
298 bounding.set_x(overlay_area.right() - actual_width);
299 bounding.set_width(actual_width);
300 break;
301 }
302
303 DCHECK_LE(bounding.right(), overlay_area.right());
304
305 switch (y) {
306 case PageOverlays::BOTTOM:
307 bounding.set_y(overlay_area.bottom() - string_size.height());
308 break;
309 case PageOverlays::TOP:
310 bounding.set_y(overlay_area.y());
311 break;
312 }
313
[email protected]43456802008-11-04 16:09:52314 if (string_size.width() > bounding.width()) {
315 if (line == PageOverlays::kUrl) {
316 output = gfx::ElideUrl(url(), font, bounding.width(), std::wstring());
317 } else {
318 output = gfx::ElideText(output, font, bounding.width());
319 }
320 }
initial.commit09911bf2008-07-26 23:55:29321
322 // Save the state (again) for the clipping region.
323 int saved_state = SaveDC(context);
324 DCHECK_NE(saved_state, 0);
325
326 int result = IntersectClipRect(context, bounding.x(), bounding.y(),
327 bounding.right() + 1, bounding.bottom() + 1);
328 DCHECK(result == SIMPLEREGION || result == COMPLEXREGION);
329 TextOut(context,
330 bounding.x(), bounding.y(),
331 output.c_str(),
332 static_cast<int>(output.size()));
333 int res = RestoreDC(context, saved_state);
334 DCHECK_NE(res, 0);
335}
336
337void PrintedDocument::PrintPage_ThreadJump(int page_number) {
338 if (MessageLoop::current() != immutable_.source_message_loop_) {
339 immutable_.source_message_loop_->PostTask(FROM_HERE, NewRunnableMethod(
340 this, &PrintedDocument::PrintPage_ThreadJump, page_number));
341 } else {
342 PrintedPagesSource* source = NULL;
343 {
344 AutoLock lock(lock_);
345 source = mutable_.source_;
346 }
[email protected]718fb432009-02-05 19:20:36347 NOTREACHED();
initial.commit09911bf2008-07-26 23:55:29348 }
349}
350
351PrintedDocument::Mutable::Mutable(PrintedPagesSource* source)
352 : source_(source),
353 expected_page_count_(0),
354 page_count_(0),
355 shrink_factor(0) {
356}
357
358PrintedDocument::Immutable::Immutable(const PrintSettings& settings,
359 PrintedPagesSource* source,
360 int cookie)
361 : settings_(settings),
362 source_message_loop_(MessageLoop::current()),
363 name_(source->RenderSourceName()),
364 url_(source->RenderSourceUrl()),
365 cookie_(cookie) {
366 // Setup the document's date.
367#ifdef WIN32
368 // On Windows, use the native time formatting for printing.
369 SYSTEMTIME systemtime;
370 GetLocalTime(&systemtime);
371 date_ = win_util::FormatSystemDate(systemtime, std::wstring());
372 time_ = win_util::FormatSystemTime(systemtime, std::wstring());
373#else
374 Time now = Time::Now();
375 date_ = TimeFormat::ShortDateNumeric(now);
376 time_ = TimeFormat::TimeOfDay(now);
377#endif // WIN32
378}
379
380} // namespace printing
license.botbf09a502008-08-24 00:55:55381