[Windows][Node.js][TypeScript] Get installed printer driver names
Tags: node, typescript, windows
Published: 2021-05-23 00:10:02

## Intro
I wanted to print PDF files.
Before printing, I wanted to search installed printer driver names and choose one to use.
But because I got some problems when I tried getting printer names, I will note how to avoid them.

### Environments
* Windows 10 ver.20H2
* Node.js ver.16.2.0
* TypeScript ver.4.2.4
* pdf-to-printer ver.2.0.4
* iconv-lite ver.0.4.24

## Getting printer driver names
I can get installed printer driver names by "pdf-to-printer".

* [GitHub - artiebits/pdf-to-printer: Print PDF files from Node.js and Electron](https://github.com/artiebits/pdf-to-printer)

```ts
import ptp from "pdf-to-printer";
async function print()
{
    const printerName = 'Microsoft Print to PDF';
    const printers = await ptp.getPrinters();
    for(const p of printers)
    {
        console.log(`PRINTER Name: ${p.name} compare: ${p.name === printerName}`);
    }
}
print();
```
#### Result
```
PRINTER Name: OneNote for Windows 10 compare: false
PRINTER Name: OneNote (??????) compare: false
PRINTER Name: OneNote (Desktop) compare: false
PRINTER Name: Microsoft XPS Document Writer compare: false
PRINTER Name: Microsoft Print to PDF compare: true
PRINTER Name: Fax compare: false
```
### Problem
Actualy, the second printer driver name was "OneNote （デスクトップ）".

Because the name was garbled, all of the comparing will be failed.
And I will get an error if I do like below.

```ts
...
    const printers = await ptp.getPrinters();
    for(const p of printers)
    {
        if(p.name.startsWith('OneNote ('))
        {
            console.log(p.name);        
            ptp.print('C:/Users/example/OneDrive/Documents/workspace/print-pdf-sample/sample.pdf',
            {
                printer: p.name,
            })
            .then(_ => console.log('OK'))
            .catch(error => console.error(error));
            break;
        }
    }
...
```
#### Result
```
OneNote (??????)
Error: Command failed: C:\Users\example\OneDrive\Documents\workspace\print-pdf-sample\node_modules\pdf-to-printer\dist\SumatraPDF.exe -print-to OneNote (??????) -silent C:/Users/example/OneDrive/Documents/workspace/print-pdf-sample/sample.pdf

    at ChildProcess.exithandler (node:child_process:326:12)
    at ChildProcess.emit (node:events:365:28)
    at maybeClose (node:internal/child_process:1067:16)
    at Process.ChildProcess._handle.onexit (node:internal/child_process:301:5) {
  killed: false,
  code: 1,
  signal: null,
  cmd: 'C:\\Users\\example\\OneDrive\\Documents\\workspace\\print-pdf-sample\\node_modules\\pdf-to-printer\\dist\\SumatraPDF.exe -print-to OneNote (??????) -silent C:/Users/example/OneDrive/Documents/workspace/print-pdf-sample/sample.pdf'
}
```
## How to get the printer driver names?
According to the source code, "pdf-to-printer" uses "wmic printer get deviceid,name".

* [pdf-to-printer/get-printers.js at master · artiebits/pdf-to-printer · GitHub](https://github.com/artiebits/pdf-to-printer/blob/master/src/win32/get-printers.js)

Even if I execute the command directly in PowerShell, the name is also garbled.

### Change the text encodings (failed)
I can run commands by "exec", "execFile" of "child_process".

* [Child process | Node.js v16.2.0 Documentation](https://nodejs.org/api/child_process.html#:~:text=child_process.execFileSync%20%28%29%3A%20a%20synchronous%20version%20of%20child_process.execFile%20%28%29,scripts%2C%20the%20synchronous%20counterparts%20may%20be%20more%20convenient.)

```ts
async function print()
{
    execFile ("powershell", [`Get-Printer`], { encoding: 'utf8'}, (error, stdout) =>{
        console.log("execFile");
        console.log(stdout);
        console.log(error);
    });
    exec('powershell Get-Printer', (error: any, stdout: any, strerr: any) => {
        console.log("exec");
        console.log(stdout);
        console.log(error);
    });
...
}
```

I also try changing text encodings by "iconv-lite" or some other way.

```ts
async function print()
{
    execFile ("powershell", [`Get-Printer`], { encoding: 'utf8'}, (error, stdout) =>{
        console.log("execFile");
        const buf = Buffer.from(stdout, 'binary');
        const dec = iconv.decode(buf, 'UTF-8');
        console.log(dec);
        console.log(error);
    });
    exec('powershell $OutputEncoding = [Text.UTF8Encoding]::UTF8;Get-Printer', (error: any, stdout: any, strerr: any) => {
        console.log("Exec");
        console.log(stdout);
        console.log(error);
    });
...
}
```

But the results aren't changed anything.
I think it's because the texts has been garbled before I get them.

## Change OS encodings
This problem comes from the terminals text encodings aren't "UTF-8".
So I change them from the settings.

### Steps
1. Open Settings > Time & Language > Region > Additional date, time & regional settings > Region.
2. Click "Change system locale..." in "Administrative" tab.
3. Check "Beta: Use Unicode UTF-8 for worldwide language support" 
4. Restart the computer

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3c4l8vhauyaqdpcxjn9e.png)

After that, I can get Japanese printer driver names.

```ts
async function print()
{
    execFile ("powershell", [`Get-Printer`], { encoding: 'utf8'}, (error, stdout) =>{
        console.log("execFile");
        console.log(stdout);
        console.log(error);
    });
    const printers = await ptp.getPrinters();
    for(const p of printers)
    {
        console.log(`PRINTER Name: ${p.name}`);
    }
...
}
```
#### Results
```
PRINTER Name: OneNote (Desktop)
PRINTER Name: OneNote for Windows 10
PRINTER Name: OneNote （デスクトップ）
PRINTER Name: Microsoft XPS Document Writer
PRINTER Name: Microsoft Print to PDF
PRINTER Name: Fax
execFile

Name                           ComputerName    Type         DriverName                PortName        Shared   Publishe
                                                                                                               d
----                           ------------    ----         ----------                --------        ------   --------
OneNote (Desktop)                              Local        Send to Microsoft OneN... nul:            False    False
OneNote for Windows 10                         Local        Microsoft Software Pri... Microsoft.Of... False    False
OneNote （デスクトップ）                               Local        Send to Microsoft OneN... nul:            False    False
Microsoft XPS Document Writer                  Local        Microsoft XPS Document... PORTPROMPT:     False    False
Microsoft Print to PDF                         Local        Microsoft Print To PDF    PORTPROMPT:     False    False
Fax                                            Local        Microsoft Shared Fax D... SHRFAX:         False    False
```

* [Powershellで日本語が使いたい - Qiita](https://qiita.com/zawazawa5809/items/d6f84cf92a56f47229ed)