summaryrefslogtreecommitdiff
path: root/chromium/printing/print_settings_initializer_win.cc
blob: 84a4a4f17ecd08cadb6a5c5c0ce85ec48c3f7b7e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "printing/print_settings_initializer_win.h"

#include <windows.h>

#include "printing/backend/win_helper.h"
#include "printing/mojom/print.mojom.h"
#include "printing/print_settings.h"

namespace printing {

namespace {

bool HasEscapeSupport(HDC hdc, DWORD escape) {
  const char* ptr = reinterpret_cast<const char*>(&escape);
  return ExtEscape(hdc, QUERYESCSUPPORT, sizeof(escape), ptr, 0, nullptr) > 0;
}

bool IsTechnology(HDC hdc, const char* technology) {
  if (::GetDeviceCaps(hdc, TECHNOLOGY) != DT_RASPRINTER)
    return false;

  if (!HasEscapeSupport(hdc, GETTECHNOLOGY))
    return false;

  char buf[256];
  memset(buf, 0, sizeof(buf));
  if (ExtEscape(hdc, GETTECHNOLOGY, 0, nullptr, sizeof(buf) - 1, buf) <= 0)
    return false;
  return strcmp(buf, technology) == 0;
}

void SetPrinterToGdiMode(HDC hdc) {
  // Try to set to GDI centric mode
  DWORD mode = PSIDENT_GDICENTRIC;
  const char* ptr = reinterpret_cast<const char*>(&mode);
  ExtEscape(hdc, POSTSCRIPT_IDENTIFY, sizeof(DWORD), ptr, 0, nullptr);
}

int GetPrinterPostScriptLevel(HDC hdc) {
  constexpr int param = FEATURESETTING_PSLEVEL;
  const char* param_char_ptr = reinterpret_cast<const char*>(&param);
  int param_out = 0;
  char* param_out_char_ptr = reinterpret_cast<char*>(&param_out);
  if (ExtEscape(hdc, GET_PS_FEATURESETTING, sizeof(param), param_char_ptr,
                sizeof(param_out), param_out_char_ptr) > 0) {
    return param_out;
  }
  return 0;
}

bool IsPrinterPostScript(HDC hdc, int* level) {
  static constexpr char kPostScriptDriver[] = "PostScript";

  // If printer does not support POSTSCRIPT_IDENTIFY, it cannot be set to GDI
  // mode to check the language level supported. See if it looks like a
  // postscript printer and supports the postscript functions that are
  // supported in compatability mode. If so set to level 2 postscript.
  if (!HasEscapeSupport(hdc, POSTSCRIPT_IDENTIFY)) {
    if (!IsTechnology(hdc, kPostScriptDriver))
      return false;
    if (!HasEscapeSupport(hdc, POSTSCRIPT_PASSTHROUGH) ||
        !HasEscapeSupport(hdc, POSTSCRIPT_DATA)) {
      return false;
    }
    *level = 2;
    return true;
  }

  // Printer supports POSTSCRIPT_IDENTIFY so we can assume it has a postscript
  // driver. Set the printer to GDI mode in order to query the postscript
  // level. Use GDI mode instead of PostScript mode so that if level detection
  // fails or returns language level < 2 we can fall back to normal printing.
  // Note: This escape must be called before other escapes.
  SetPrinterToGdiMode(hdc);
  if (!HasEscapeSupport(hdc, GET_PS_FEATURESETTING)) {
    // Can't query the level, use level 2 to be safe
    *level = 2;
    return true;
  }

  // Get the language level. If invalid or < 2, return false to set printer to
  // normal printing mode.
  *level = GetPrinterPostScriptLevel(hdc);
  return *level == 2 || *level == 3;
}

bool IsPrinterXPS(HDC hdc) {
  static constexpr char kXPSDriver[] =
      "http://schemas.microsoft.com/xps/2005/06";
  return IsTechnology(hdc, kXPSDriver);
}

bool IsPrinterTextOnly(HDC hdc) {
  return ::GetDeviceCaps(hdc, TECHNOLOGY) == DT_CHARSTREAM;
}
}  // namespace

// static
void PrintSettingsInitializerWin::InitPrintSettings(
    HDC hdc,
    const DEVMODE& dev_mode,
    PrintSettings* print_settings) {
  DCHECK(hdc);
  DCHECK(print_settings);

  print_settings->SetOrientation(dev_mode.dmOrientation == DMORIENT_LANDSCAPE);
  int dpi_x = GetDeviceCaps(hdc, LOGPIXELSX);
  int dpi_y = GetDeviceCaps(hdc, LOGPIXELSY);
  print_settings->set_dpi_xy(dpi_x, dpi_y);
  const int kAlphaCaps = SB_CONST_ALPHA | SB_PIXEL_ALPHA;
  print_settings->set_supports_alpha_blend(
      (GetDeviceCaps(hdc, SHADEBLENDCAPS) & kAlphaCaps) == kAlphaCaps);

  DCHECK_EQ(GetDeviceCaps(hdc, SCALINGFACTORX), 0);
  DCHECK_EQ(GetDeviceCaps(hdc, SCALINGFACTORY), 0);

  // Initialize `page_setup_device_units_`.
  // Blink doesn't support different dpi settings in X and Y axis. However,
  // some printers use them. So, to avoid a bad page calculation, scale page
  // size components based on the dpi in the appropriate dimension.
  int dpi = print_settings->dpi();
  gfx::Size physical_size_device_units(
      GetDeviceCaps(hdc, PHYSICALWIDTH) * dpi / dpi_x,
      GetDeviceCaps(hdc, PHYSICALHEIGHT) * dpi / dpi_y);
  gfx::Rect printable_area_device_units(
      GetDeviceCaps(hdc, PHYSICALOFFSETX) * dpi / dpi_x,
      GetDeviceCaps(hdc, PHYSICALOFFSETY) * dpi / dpi_y,
      GetDeviceCaps(hdc, HORZRES) * dpi / dpi_x,
      GetDeviceCaps(hdc, VERTRES) * dpi / dpi_y);

  // Sanity check the printable_area: we've seen crashes caused by a printable
  // area rect of 0, 0, 0, 0, so it seems some drivers don't set it.
  if (printable_area_device_units.IsEmpty() ||
      !gfx::Rect(physical_size_device_units)
           .Contains(printable_area_device_units)) {
    printable_area_device_units = gfx::Rect(physical_size_device_units);
  }
  DCHECK_EQ(print_settings->device_units_per_inch(), dpi);
  print_settings->SetPrinterPrintableArea(physical_size_device_units,
                                          printable_area_device_units, false);

  print_settings->set_color(IsDevModeWithColor(&dev_mode)
                                ? mojom::ColorModel::kColor
                                : mojom::ColorModel::kGray);

  // Check for postscript first so that we can change the mode with the
  // first command.
  int level;
  if (IsPrinterPostScript(hdc, &level)) {
    if (level == 2) {
      print_settings->set_printer_language_type(
          mojom::PrinterLanguageType::kPostscriptLevel2);
      return;
    }
    DCHECK_EQ(3, level);
    print_settings->set_printer_language_type(
        mojom::PrinterLanguageType::kPostscriptLevel3);
    return;
  }
  // Detects the generic / text only driver.
  if (IsPrinterTextOnly(hdc)) {
    print_settings->set_printer_language_type(
        mojom::PrinterLanguageType::kTextOnly);
    return;
  }
  if (IsPrinterXPS(hdc)) {
    print_settings->set_printer_language_type(mojom::PrinterLanguageType::kXps);
    return;
  }
  print_settings->set_printer_language_type(mojom::PrinterLanguageType::kNone);
}

}  // namespace printing