1

Here's a sample script:

import os
import sys
import pathlib
import json
from contextlib import redirect_stderr
from fontTools import ttLib
from fontmeta import FontMeta

# Check for commandline argument
if len(sys.argv) == 1:
    print('No argument was supplied.')
    exit(0)

fontfile = sys.argv[1]


def font_name(font_path, name_idx):
    font = ttLib.TTFont(font_path, ignoreDecompileErrors=True)
    with redirect_stderr(None):
        names = font['name'].names

    details = {}
    for x in names:
        if x.langID == 0 or x.langID == 1033:
            try:
                details[x.nameID] = x.toUnicode()
            except UnicodeDecodeError:
                details[x.nameID] = x.string.decode(errors='ignore')
    # details[4] = Full Name
    # details[1] = Family Name
    # details[2] = Style Name
    return details[name_idx]


meta_instance = FontMeta(fontfile)

metadata = meta_instance.get_full_data()

fontFullName           = font_name(fontfile,4)
fontFamily             = font_name(fontfile,1)
fontStyle              = font_name(fontfile,2)
fontVers               = metadata[5]['value'];
fontVers               = fontVers.replace('Version ',"v")
fontLang               = metadata[1]['language']['value'];
fontUniqueID           = metadata[3]['value']
fontPostscriptName     = metadata[6]['value']
fontPostscriptEncoding = metadata[6]['encoding']['value']
fontDesigner           = metadata[9]['value']
fontLicenseURL         = metadata[14]['value']

print('Full Name:     ' + fontFullName)
print('Family:        ' + fontFamily)
print('Style:         ' + fontStyle)
print('Version:       ' + fontVers)
print('Language:      ' + fontLang)
print('UniqueID:      ' + fontUniqueID)
print('License URL:   ' + fontLicenseURL)
print('Font Designer: ' + fontDesigner)

Output:

Full Name:     Sharp Sans Bold
Family:        Sharp Sans
Style:         Bold
Version:       v1.001
Language:      English/United States
UniqueID:      1.001;2016;SHRP;SharpSans-Bold
License URL:   http://www.sharptype.co
Font Designer: Lucas Sharp

ps1:

& "D:\Dev\Python\00 VENV\FontTools\Scripts\Activate.ps1"

$val = python "D:\Dev\Python\Font Scripts\GetFontInfo.py" "D:\Fonts\00 Test\SharpSans-Bold.otf"

Write-Host "`$val:" $val -ForegroundColor Green

Right now the Python code is just printing values. My PS script is echoing the printed values as a string. Is there a way to pass these values to powershell other than just printing them - I.E. as an array?

Or should I return JSON and parse it in PowerShell?

Any help appreciated.

fmotion1
  • 237
  • 4
  • 12

2 Answers2

1

When PowerShell invokes an external program, it streams the individual lines of its stdout output one by one to PowerShell's pipeline.

If you capture this stream via assignment to a variable:

  • two or more lines become an array (of type [object[]]) storing the lines.

  • one line is stored as-is, as a string (type [string]).

Therefore:

$val = python "D:\Dev\Python\Font Scripts\GetFontInfo.py" "D:\Fonts\00 Test\SharpSans-Bold.otf"

by itself is sufficient to capture the stdout output lines from your python call as an array - assuming there are two or more lines.

If you want to ensure that $val is always an array - even if there's only one output line, you have two options:

$val = @(python "D:\Dev\Python\Font Scripts\GetFontInfo.py" "D:\Fonts\00 Test\SharpSans-Bold.otf")
  • Or: Use an [array] type constraint (equivalent to [object[]]):
[array] $val = python "D:\Dev\Python\Font Scripts\GetFontInfo.py" "D:\Fonts\00 Test\SharpSans-Bold.otf"

Note:

  • There are subtle differences between the two approaches above - see this answer for details.

  • When PowerShell captures or redirects output from external programs, decoding into .NET strings based on the character encoding stored in [Console]::OutputEncoding is invariably involved - see this answer.

  • To verify that $val is indeed an array, run $val.GetType().FullName.

  • To print each array element on it own line, do not use Write-Host, simply submit $val on its own (which is equivalent to using Write-Output $val, though the latter's explicit use is rarely needed - see this answer for background information.

mklement0
  • 382,024
  • 64
  • 607
  • 775
0

You can print the values line by line and split the output by newline as instructed here:

$arr = $val.Split([Environment]::NewLine)
Vahid Haratian
  • 708
  • 2
  • 6
  • 23
  • 1
    `$val = python ...` - due to capturing the output from a call to an _external program_ - _by default_ stores an _array of lines_ in `$val` (assuming two or more output lines). Your solution is an unnecessary and expensive no-op, because - due to [member-access enumeration](https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_Member-Access_Enumeration) - it makes the `.Split()` call on _each and every output line_, and each such line by definition does not contain any newlines. – mklement0 Aug 04 '23 at 02:14