Skip to content

Commit

Permalink
Some printers has issue with metafiles produces from PDF so added swi…
Browse files Browse the repository at this point in the history
…tch and about:flag as workaround for affected users.

Rasterisation is implemented and RasterizeMetafile function.
It replace metafile with new one where content is only bitmap created by playback of original metafile.

FlattenTransparency replaced with full rasterisation.

Fixed rectangle returned by Emf::GetPageBounds.

Removed SkDevice::AlphaBlendUsed().

Removed return value from PrintWebViewHelper::RenderPage on windows.

BUG=133527

Review URL: https://chromiumcodereview.appspot.com/10836330

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@152681 0039d316-1c4b-4281-b951-d872f2087c98
  • Loading branch information
vitalybuka@chromium.org committed Aug 22, 2012
1 parent f26c770 commit 60d77bd
Show file tree
Hide file tree
Showing 14 changed files with 201 additions and 189 deletions.
8 changes: 8 additions & 0 deletions chrome/app/generated_resources.grd
Original file line number Diff line number Diff line change
Expand Up @@ -5361,6 +5361,14 @@ Keep your key file in a safe place. You will need it to create new versions of y
<message name="IDS_FLAGS_PRINT_SETTING_RESET_DESCRIPTION" desc="Description for flag to reset the last printer and settings on browser restart.">
Reset the last used printer on restart. With this enabled, the OS default printer will become the current printer for print preview on each browser start.
</message>
<if expr="is_win">
<message name="IDS_FLAGS_PRINT_RASTER_NAME" desc="Name of 'Print raster' flag.">
Print raster
</message>
<message name="IDS_FLAGS_PRINT_RASTER_DESCRIPTION" desc="Description for 'Print raster' flag.">
Rasterise page before printing. Slower, but may help to resolve issues with some printers
</message>
</if>
<message name="IDS_FLAGS_CRXLESS_WEB_APPS_NAME" desc="Title of the CRX-less web apps lab">
CRX-less Web Apps
</message>
Expand Down
9 changes: 9 additions & 0 deletions chrome/browser/about_flags.cc
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,15 @@ const Experiment kExperiments[] = {
kOsAll,
SINGLE_VALUE_TYPE(switches::kPrintSettingsReset)
},
#if defined(OS_WIN)
{
"print-raster",
IDS_FLAGS_PRINT_RASTER_NAME,
IDS_FLAGS_PRINT_RASTER_DESCRIPTION,
kOsWin,
SINGLE_VALUE_TYPE(switches::kPrintRaster)
},
#endif // OS_WIN
{
"crxless-web-apps",
IDS_FLAGS_CRXLESS_WEB_APPS_NAME,
Expand Down
43 changes: 30 additions & 13 deletions chrome/browser/printing/print_view_manager.cc
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include <map>

#include "base/bind.h"
#include "base/command_line.h"
#include "base/lazy_instance.h"
#include "base/memory/scoped_ptr.h"
#include "base/metrics/histogram.h"
Expand All @@ -24,6 +25,7 @@
#include "chrome/browser/ui/tab_contents/tab_contents.h"
#include "chrome/browser/ui/webui/print_preview/print_preview_ui.h"
#include "chrome/common/chrome_notification_types.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/pref_names.h"
#include "chrome/common/print_messages.h"
#include "content/public/browser/browser_thread.h"
Expand Down Expand Up @@ -52,6 +54,9 @@ typedef std::map<content::RenderProcessHost*, base::Closure>
static base::LazyInstance<ScriptedPrintPreviewClosureMap>
g_scripted_print_preview_closure_map = LAZY_INSTANCE_INITIALIZER;

// Limits memory usage by raster to 64 MiB.
const int kMaxRasterSizeInPixels = 16*1024*1024;

} // namespace

namespace printing {
Expand Down Expand Up @@ -221,18 +226,6 @@ void PrintViewManager::OnDidPrintPage(
return;
}

#if defined(OS_WIN)
// http://msdn2.microsoft.com/en-us/library/ms535522.aspx
// Windows 2000/XP: When a page in a spooled file exceeds approximately 350
// MB, it can fail to print and not send an error message.
if (params.data_size && params.data_size >= 350*1024*1024) {
NOTREACHED() << "size:" << params.data_size;
TerminatePrintJob(true);
web_contents()->Stop();
return;
}
#endif

#if defined(OS_WIN) || defined(OS_MACOSX)
const bool metafile_must_be_valid = true;
#elif defined(OS_POSIX)
Expand All @@ -249,7 +242,7 @@ void PrintViewManager::OnDidPrintPage(
}
}

scoped_ptr<Metafile> metafile(new NativeMetafile);
scoped_ptr<NativeMetafile> metafile(new NativeMetafile);
if (metafile_must_be_valid) {
if (!metafile->InitFromData(shared_buf.memory(), params.data_size)) {
NOTREACHED() << "Invalid metafile header";
Expand All @@ -258,6 +251,30 @@ void PrintViewManager::OnDidPrintPage(
}
}

#if defined(OS_WIN)
bool big_emf = (params.data_size && params.data_size >= kMetafileMaxSize);
const CommandLine* cmdline = CommandLine::ForCurrentProcess();
if (big_emf ||
(cmdline && cmdline->HasSwitch(switches::kPrintRaster)) ||
(!print_job_->settings().supports_alpha_blend() &&
metafile->IsAlphaBlendUsed())) {
int raster_size = std::min(params.page_size.GetArea(),
kMaxRasterSizeInPixels);
scoped_ptr<NativeMetafile> raster_metafile(
metafile->RasterizeMetafile(raster_size));
if (raster_metafile.get()) {
metafile.swap(raster_metafile);
} else if (big_emf) {
// Don't fall back to emf here.
NOTREACHED() << "size:" << params.data_size;
TerminatePrintJob(true);
web_contents()->Stop();
return;
}
}

#endif

// Update the rendered document. It will send notifications to the listener.
document->SetPage(params.page_number,
metafile.release(),
Expand Down
3 changes: 3 additions & 0 deletions chrome/common/chrome_switches.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1082,6 +1082,9 @@ const char kPrerenderModeSwitchValueEnabled[] = "enabled";
// prefetch_only: No prerendering, but enables prefetching.
const char kPrerenderModeSwitchValuePrefetchOnly[] = "prefetch_only";

// Enable conversion from vector to raster for any page.
const char kPrintRaster[] = "print-raster";

// Disable saving the printer and settings between sessions.
const char kPrintSettingsReset[] = "print-settings-reset";

Expand Down
1 change: 1 addition & 0 deletions chrome/common/chrome_switches.h
Original file line number Diff line number Diff line change
Expand Up @@ -437,6 +437,7 @@ extern const char kUseMockKeychain[];
extern const char kDisableDesktopShortcuts[];
extern const char kEnableSyncCredentialCaching[];
extern const char kForceImmersive[];
extern const char kPrintRaster[];
extern const char kRelaunchShortcut[];
extern const char kWaitForHandle[];
#endif
Expand Down
20 changes: 8 additions & 12 deletions chrome/renderer/print_web_view_helper.h
Original file line number Diff line number Diff line change
Expand Up @@ -269,18 +269,14 @@ class PrintWebViewHelper

// Platform specific helper function for rendering page(s) to |metafile|.
#if defined(OS_WIN)
// Because of mixed support for alpha channels on printers, this method may
// need to create a new metafile. The result may be either the passed
// |metafile| or a new one. In either case, the caller owns both |metafile|
// and the result.
printing::Metafile* RenderPage(const PrintMsg_Print_Params& params,
int page_number,
WebKit::WebFrame* frame,
bool is_preview,
printing::Metafile* metafile,
double* scale_factor,
gfx::Size* page_size_in_dpi,
gfx::Rect* content_area_in_dpi);
void RenderPage(const PrintMsg_Print_Params& params,
int page_number,
WebKit::WebFrame* frame,
bool is_preview,
printing::Metafile* metafile,
double* scale_factor,
gfx::Size* page_size_in_dpi,
gfx::Rect* content_area_in_dpi);
#elif defined(OS_MACOSX)
void RenderPage(const PrintMsg_Print_Params& params, int page_number,
WebKit::WebFrame* frame, bool is_preview,
Expand Down
141 changes: 6 additions & 135 deletions chrome/renderer/print_web_view_helper_win.cc
Original file line number Diff line number Diff line change
Expand Up @@ -32,117 +32,6 @@ using printing::kPointsPerInch;
using printing::Metafile;
using WebKit::WebFrame;

namespace {

int CALLBACK EnhMetaFileProc(HDC dc,
HANDLETABLE* handle_table,
const ENHMETARECORD *record,
int num_objects,
LPARAM data) {
HDC* bitmap_dc = reinterpret_cast<HDC*>(data);
// Play this command to the bitmap DC.
PlayEnhMetaFileRecord(*bitmap_dc, handle_table, record, num_objects);
switch (record->iType) {
case EMR_ALPHABLEND: {
const EMRALPHABLEND* emr_alpha_blend =
reinterpret_cast<const EMRALPHABLEND*>(record);
XFORM bitmap_dc_transform, metafile_dc_transform;
XFORM identity = { 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f };
// Temporarily set the world transforms of both DC's to identity.
GetWorldTransform(dc, &metafile_dc_transform);
SetWorldTransform(dc, &identity);
GetWorldTransform(*bitmap_dc, &bitmap_dc_transform);
SetWorldTransform(*bitmap_dc, &identity);
const RECTL& rect = emr_alpha_blend->rclBounds;
// Since the printer does not support alpha blend, copy the alpha
// blended region from our (software-rendered) bitmap DC to the
// metafile DC.
BitBlt(dc,
rect.left,
rect.top,
rect.right - rect.left + 1,
rect.bottom - rect.top + 1,
*bitmap_dc,
rect.left,
rect.top,
SRCCOPY);
// Restore the world transforms of both DC's.
SetWorldTransform(dc, &metafile_dc_transform);
SetWorldTransform(*bitmap_dc, &bitmap_dc_transform);
break;
}

case EMR_CREATEBRUSHINDIRECT:
case EMR_CREATECOLORSPACE:
case EMR_CREATECOLORSPACEW:
case EMR_CREATEDIBPATTERNBRUSHPT:
case EMR_CREATEMONOBRUSH:
case EMR_CREATEPALETTE:
case EMR_CREATEPEN:
case EMR_DELETECOLORSPACE:
case EMR_DELETEOBJECT:
case EMR_EXTCREATEFONTINDIRECTW:
// Play object creation command only once.
break;

default:
// Play this command to the metafile DC.
PlayEnhMetaFileRecord(dc, handle_table, record, num_objects);
break;
}
return 1; // Continue enumeration
}

Metafile* FlattenTransparency(Metafile* metafile, const gfx::Size& page_size) {
// Currently, we handle alpha blend transparency for a single page.
// Therefore, expecting a metafile with page count 1.
DCHECK_EQ(1U, metafile->GetPageCount());

// Close the device context to retrieve the compiled metafile.
if (!metafile->FinishDocument())
NOTREACHED();

// Page used alpha blend, but printer doesn't support it. Rewrite the
// metafile and flatten out the transparency.
base::win::ScopedGetDC screen_dc(NULL);
base::win::ScopedCreateDC bitmap_dc(CreateCompatibleDC(screen_dc));
if (!bitmap_dc)
NOTREACHED() << "Bitmap DC creation failed";
SetGraphicsMode(bitmap_dc, GM_ADVANCED);
void* bits = NULL;
BITMAPINFO hdr;
gfx::CreateBitmapHeader(page_size.width(), page_size.height(),
&hdr.bmiHeader);
base::win::ScopedBitmap hbitmap(CreateDIBSection(
bitmap_dc, &hdr, DIB_RGB_COLORS, &bits, NULL, 0));
if (!hbitmap)
NOTREACHED() << "Raster bitmap creation for printing failed";

base::win::ScopedSelectObject selectBitmap(bitmap_dc, hbitmap);
RECT rect = { 0, 0, page_size.width(), page_size.height() };
HBRUSH whiteBrush = static_cast<HBRUSH>(GetStockObject(WHITE_BRUSH));
FillRect(bitmap_dc, &rect, whiteBrush);

Metafile* metafile2(new printing::NativeMetafile);
metafile2->Init();
HDC hdc = metafile2->context();
DCHECK(hdc);
skia::InitializeDC(hdc);

RECT metafile_bounds = metafile->GetPageBounds(1).ToRECT();
// Process the old metafile, placing all non-AlphaBlend calls into the
// new metafile, and copying the results of all the AlphaBlend calls
// from the bitmap DC.
EnumEnhMetaFile(hdc,
metafile->emf(),
EnhMetaFileProc,
&bitmap_dc,
&metafile_bounds);
return metafile2;
}

} // namespace

void PrintWebViewHelper::PrintPageInternal(
const PrintMsg_PrintPage_Params& params,
const gfx::Size& canvas_size,
Expand All @@ -166,9 +55,8 @@ void PrintWebViewHelper::PrintPageInternal(
gfx::Rect content_area_in_dpi;

// Render page for printing.
metafile.reset(RenderPage(params.params, page_number, frame, false,
metafile.get(), &actual_shrink, &page_size_in_dpi,
&content_area_in_dpi));
RenderPage(params.params, page_number, frame, false, metafile.get(),
&actual_shrink, &page_size_in_dpi, &content_area_in_dpi);

// Close the device context to retrieve the compiled metafile.
if (!metafile->FinishDocument())
Expand Down Expand Up @@ -210,11 +98,8 @@ bool PrintWebViewHelper::RenderPreviewPage(int page_number) {
}

base::TimeTicks begin_time = base::TimeTicks::Now();
printing::Metafile* render_page_result =
RenderPage(print_params, page_number, print_preview_context_.frame(),
true, initial_render_metafile, &actual_shrink, NULL, NULL);
// In the preview flow, RenderPage will never return a new metafile.
DCHECK_EQ(render_page_result, initial_render_metafile);
RenderPage(print_params, page_number, print_preview_context_.frame(), true,
initial_render_metafile, &actual_shrink, NULL, NULL);
print_preview_context_.RenderedPreviewPage(
base::TimeTicks::Now() - begin_time);

Expand All @@ -229,7 +114,7 @@ bool PrintWebViewHelper::RenderPreviewPage(int page_number) {
return PreviewPageRendered(page_number, draft_metafile.get());
}

Metafile* PrintWebViewHelper::RenderPage(
void PrintWebViewHelper::RenderPage(
const PrintMsg_Print_Params& params, int page_number, WebFrame* frame,
bool is_preview, Metafile* metafile, double* actual_shrink,
gfx::Size* page_size_in_dpi, gfx::Rect* content_area_in_dpi) {
Expand Down Expand Up @@ -319,27 +204,13 @@ Metafile* PrintWebViewHelper::RenderPage(

bool result = metafile->FinishPage();
DCHECK(result);

if (!params.supports_alpha_blend) {
// PreviewMetafile (PDF) supports alpha blend, so we only hit this case
// for NativeMetafile.
DCHECK(!is_preview);
skia::PlatformDevice* platform_device = skia::GetPlatformDevice(device);
if (platform_device && platform_device->AlphaBlendUsed()) {
return FlattenTransparency(metafile, page_size);
}
}
return metafile;
}

bool PrintWebViewHelper::CopyMetafileDataToSharedMem(
Metafile* metafile, base::SharedMemoryHandle* shared_mem_handle) {
uint32 buf_size = metafile->GetDataSize();
base::SharedMemory shared_buf;
// http://msdn2.microsoft.com/en-us/library/ms535522.aspx
// Windows 2000/XP: When a page in a spooled file exceeds approximately 350
// MB, it can fail to print and not send an error message.
if (buf_size >= 350*1024*1024) {
if (buf_size >= printing::kMetafileMaxSize) {
NOTREACHED() << "Buffer too large: " << buf_size;
return false;
}
Expand Down
Loading

0 comments on commit 60d77bd

Please sign in to comment.