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

printing / print_settings_initializer_win.cc [blame]

// Copyright 2011 The Chromium Authors
// 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"
#include "printing/printing_utils.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*>(¶m);
  int param_out = 0;
  char* param_out_char_ptr = reinterpret_cast<char*>(¶m_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);
  int dpi = print_settings->dpi();
  DCHECK_EQ(print_settings->device_units_per_inch(), dpi);

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

  // 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.
  gfx::Size physical_size_scaled(
      GetDeviceCaps(hdc, PHYSICALWIDTH) * dpi / dpi_x,
      GetDeviceCaps(hdc, PHYSICALHEIGHT) * dpi / dpi_y);
  gfx::Rect printable_area_device_units = GetPrintableAreaDeviceUnits(hdc);
  gfx::Rect printable_area_scaled =
      gfx::Rect(printable_area_device_units.x() * dpi / dpi_x,
                printable_area_device_units.y() * dpi / dpi_y,
                printable_area_device_units.width() * dpi / dpi_x,
                printable_area_device_units.height() * dpi / dpi_y);
  print_settings->SetPrinterPrintableArea(physical_size_scaled,
                                          printable_area_scaled, 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