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