Search code examples
pythonpython-3.xmodulevirtualenvfontforge

Can't get FontForge to import as a module in a custom Python script


I have the following script:

import fontforge
import os.path
import sys

if len(sys.argv) < 3:
    print("Usage: [FONT] [OUTPUTDIR]")
    exit(1)

fontpath = sys.argv[1]
font     = fontforge.open(fontpath)
outdir   = sys.argv[2]

if os.path.exists(outdir):
    pass
elif os.access(os.path.dirname(outdir), os.W_OK):
    os.makedirs(outdir)
else:
    exit(1)

for name in font:
    filename = name + ".svg"
    outfull = os.path.join(outdir,filename)
    font[name].export(outfull)

The script dumps all glyphs to individual SVG files in a folder.

Anyway, I've installed the latest windows version of FontForge.

From what I understand, FontForge embeds python, and the binary is ffpython.exe. So you should be able to call ffpython myscript.py {arg1 arg2 arg3}

The problem is that this doesn't work.

When I do ffpython myscript.py arg1 arg2, I get an error:

ImportError: DLL load failed while importing fontforge: The specified procedure could not be found.

You can also result with the same error by simply invoking ffpython and then typing import fontforge directly into the interpreter.

There is a batch file that comes with FontForge located at C:\Program Files (x86)\FontForgeBuilds\fontforge-console.bat.

If I run this batch file, it sets some variables and then opens a console window. If I then call ffpython in that console window and enter import fontforge, everything magically works as expected.

But this is a really limited and improper solution. I don't want to have to open this magical batch file every time I want to run a Python script that interfaces with FontForge.

So here's my question:

How can I get FontForge properly installed as a module in Python? How can I get a python script to import the fontforge module when being run via ffpython?

I would greatly appreciate any help at all.

Edit:

Running the test script by @CristiFati from an Admin PowerShell prompt where the PWD is not the same as ffpython (Failure):

03-14 20:06:09 D:\> & 'C:\Program Files (x86)\FontForgeBuilds\bin\ffpython.exe' .\code00.py
Executable: C:\Program Files (x86)\FontForgeBuilds\bin\ffpython.exe
Version: 3.10.9 (main, Dec 10 2022, 09:16:22)  [GCC 12.2.0 32 bit]
CWD: D:\
UName: None
PATH: C:\Program Files\PowerShell\7;C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v11.0\bin;C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v11.0\libnvvp;C:\Program Files (x86)\Common Files\Intel\Shared Libraries\redist\intel64_win\compiler;C:\Program Files (x86)\Common Files\Intel\Shared Files\cpp\bin\Intel64;C:\Program Files (x86)\Common Files\Intel\Shared Libraries\redist\intel64_win\compiler;C:\Program Files (x86)\Common Files\Intel\Shared Libraries\redist\ia32_win\compiler;C:\Python\Python310\Scripts\;C:\Python\Python310\;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Windows\System32\WindowsPowerShell\v1.0\;C:\Windows\System32\OpenSSH\;C:\Program Files (x86)\NVIDIA Corporation\PhysX\Common;C:\BIN;C:\Program Files\Git\cmd;C:\Program Files\GitHub CLI\;C:\Program Files\7-Zip;C:\Program Files\NVIDIA Corporation\Nsight Compute 2020.1.1;C:\Program Files\dotnet\;C:\Program Files\CMake\bin;C:\Program Files\Microsoft SQL Server\150\Tools\Binn\;C:\Program Files\PowerShell\7\;C:\Program Files (x86)\FontForgeBuilds\bin;C:\Users\myusername\AppData\Local\Microsoft\WindowsApps;C:\Users\myusername\AppData\Local\Programs\Microsoft VS Code\bin;C:\Users\myusername\.dotnet\tools;C:\Program Files (x86)\FontForgeBuilds\bin\
PYTHONHOME: None
PYTHONPATH: None
sys.path: ['D:\\', 'C:\\Program Files (x86)\\FontForgeBuilds\\lib\\python310.zip', 'C:\\Program Files (x86)\\FontForgeBuilds\\lib\\python3.10', 'C:\\Program Files (x86)\\FontForgeBuilds\\lib\\python3.10\\lib-dynload', 'D:\\', 'C:\\Program Files (x86)\\FontForgeBuilds\\lib\\python3.10\\site-packages']
Traceback (most recent call last):
  File "D:\code00.py", line 12, in <module>
    import fontforge as ff
ImportError: DLL load failed while importing fontforge: The specified procedure could not be found.
03-14 20:06:24 D:\>

Same results occur if I use CMD.

Running the test script by @CristiFati from an Admin PowerShell prompt where the PWD is C:\Program Files (x86)\FontForgeBuilds\bin (Success):

03-14 20:16:08 C:\Program Files (x86)\FontForgeBuilds\bin> ffpython D:\code00.py
Executable: C:\Program Files (x86)\FontForgeBuilds\bin\ffpython.exe
Version: 3.10.9 (main, Dec 10 2022, 09:16:22)  [GCC 12.2.0 32 bit]
CWD: C:\Program Files (x86)\FontForgeBuilds\bin
UName: None
PATH: C:\Program Files\PowerShell\7;C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v11.0\bin;C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v11.0\libnvvp;C:\Program Files (x86)\Common Files\Intel\Shared Libraries\redist\intel64_win\compiler;C:\Program Files (x86)\Common Files\Intel\Shared Files\cpp\bin\Intel64;C:\Program Files (x86)\Common Files\Intel\Shared Libraries\redist\intel64_win\compiler;C:\Program Files (x86)\Common Files\Intel\Shared Libraries\redist\ia32_win\compiler;C:\Python\Python310\Scripts\;C:\Python\Python310\;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Windows\System32\WindowsPowerShell\v1.0\;C:\Windows\System32\OpenSSH\;C:\Program Files (x86)\NVIDIA Corporation\PhysX\Common;C:\BIN;C:\Program Files\Git\cmd;C:\Program Files\GitHub CLI\;C:\Program Files\7-Zip;C:\Program Files\NVIDIA Corporation\Nsight Compute 2020.1.1;C:\Program Files\dotnet\;C:\Program Files\CMake\bin;C:\Program Files\Microsoft SQL Server\150\Tools\Binn\;C:\Program Files\PowerShell\7\;C:\Program Files (x86)\FontForgeBuilds\bin;C:\Users\myusername\AppData\Local\Microsoft\WindowsApps;C:\Users\myusername\AppData\Local\Programs\Microsoft VS Code\bin;C:\Users\myusername\.dotnet\tools;C:\Program Files (x86)\FontForgeBuilds\bin\
PYTHONHOME: None
PYTHONPATH: None
sys.path: ['D:\\', 'C:\\Program Files (x86)\\FontForgeBuilds\\lib\\python310.zip', 'C:\\Program Files (x86)\\FontForgeBuilds\\lib\\python3.10', 'C:\\Program Files (x86)\\FontForgeBuilds\\lib\\python3.10\\lib-dynload', 'C:\\Program Files (x86)\\FontForgeBuilds\\bin', 'C:\\Program Files (x86)\\FontForgeBuilds\\lib\\python3.10\\site-packages']
Python 3.10.9 (main, Dec 10 2022, 09:16:22)  [GCC 12.2.0 32 bit] 032bit on win32

<module 'fontforge' from 'C:\\Program Files (x86)\\FontForgeBuilds\\lib\\python3.10\\site-packages\\fontforge.pyd'>
['SpiroVersion', 'UnicodeAnnotationFromLib', 'UnicodeBlockCountFromLib', 'UnicodeBlockEndFromLib', 'UnicodeBlockNameFromLib', 'UnicodeBlockStartFromLib', 'UnicodeNameFromLib', 'UnicodeNames2FromLib', 'UnicodeNamesListVersion', '__date__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', '__version__', 'activeFont', 'activeFontInUI', 'activeGlyph', 'activeLayer', 'ask', 'askChoices', 'askMulti', 'askString', 'awcontext', 'awglyph', 'configurePlugins', 'contour', 'cvt', 'defaultOtherSubrs', 'font', 'fonts', 'fontsInFile', 'getConvexNib', 'getPluginInfo', 'getPrefs', 'glyph', 'glyphPen', 'hasSpiro', 'hasUserInterface', 'hooks', 'layer', 'layer_array', 'layerinfo', 'layerinfo_array', 'loadEncodingFile', 'loadNamelist', 'loadNamelistDir', 'loadPlugins', 'loadPrefs', 'logWarning', 'math', 'mathKern', 'nameFromUnicode', 'onAppClosing', 'open', 'openFilename', 'parseTTInstrs', 'point', 'postError', 'postNotice', 'preloadCidmap', 'printSetup', 'private', 'readOtherSubrsFile', 'references', 'registerGlyphSeparationHook', 'registerImportExport', 'registerMenuItem', 'runInitScripts', 'saveFilename', 'savePrefs', 'scriptFromUnicode', 'scriptPath', 'selection', 'setConvexNib', 'setPrefs', 'spiroCorner', 'spiroG2', 'spiroG4', 'spiroLeft', 'spiroOpen', 'spiroRight', 'splineCorner', 'splineCurve', 'splineHVCurve', 'splineTangent', 'unParseTTInstrs', 'unicodeFromName', 'unitShape', 'unspecifiedMathValue', 'userConfigPath', 'version']

Done.

So I need to figure out why things are only working when my PWD is C:\Program Files (x86)\FontForgeBuilds\bin. I don't understand why it won't work otherwise.


Solution

  • Just installed FontForge 20230101. Using the following test script.

    code00.py:

    #!/usr/bin/env python
    
    import os
    import sys
    
    print("Executable:", sys.executable)
    print("Version:", sys.version)
    print("CWD:", os.getcwd())
    print("UName:", getattr(os, "uname", lambda: None)())
    for evn in ("PATH", "PYTHONHOME", "PYTHONPATH"):
        print("{:s}: {:}".format(evn, os.environ.get(evn)))
    print("sys.path:", sys.path)
    
    import fontforge as ff
    
    
    def main(*argv):
        print(ff)
        print(dir(ff))
    
    
    if __name__ == "__main__":
        print("Python {:s} {:03d}bit on {:s}\n".format(" ".join(elem.strip() for elem in sys.version.split("\n")),
                                                       64 if sys.maxsize > 0x100000000 else 32, sys.platform))
        rc = main(*sys.argv[1:])
        print("\nDone.\n")
        sys.exit(rc)
    

    Output:

    [cfati@CFATI-W10PC064:e:\Work\Dev\StackExchange\StackOverflow\q073506592]> sopr.bat
    ### Set shorter prompt to better fit when pasted in StackOverflow (or other) pages ###
    
    [prompt]> :: Run FFPython (full path)
    [prompt]> "c:\Install\pc032\FontForge\FontForgeBuilds\Version\bin\ffpython.exe" ./code00.py
    Python 3.10.9 (main, Dec 10 2022, 09:16:22)  [GCC 12.2.0 32 bit] 032bit on win32
    
    Executable: c:\Install\pc032\FontForge\FontForgeBuilds\Version\bin\ffpython.exe
    Version: 3.10.9 (main, Dec 10 2022, 09:16:22)  [GCC 12.2.0 32 bit]
    CWD: E:\Work\Dev\StackExchange\StackOverflow\q073506592
    UName: None
    PATH: C:\WINDOWS\System32\WindowsPowerShell\v1.0\;C:\WINDOWS\system32;C:\WINDOWS;C:\WINDOWS\System32\Wbem;C:\WINDOWS\System32\OpenSSH\;C:\Users\cfati\AppData\Local\Programs\Python\Launcher\;e:\Work\Dev\Utils\current;e:\Work\Dev\Utils\current\Win;e:\Work\Dev\VEnvs\py_pc064_03.08.07_test0\Scripts;C:\Users\cfati\AppData\Local\Microsoft\WindowsApps;;C:\Install\pc032\FontForge\FontForgeBuilds\Version\bin\
    PYTHONHOME: None
    PYTHONPATH: None
    sys.path: ['e:\\Work\\Dev\\StackExchange\\StackOverflow\\q073506592', 'C:\\Install\\pc032\\FontForge\\FontForgeBuilds\\Version\\lib\\python310.zip', 'C:\\Install\\pc032\\FontForge\\FontForgeBuilds\\Version\\lib\\python3.10', 'C:\\Install\\pc032\\FontForge\\FontForgeBuilds\\Version\\lib\\python3.10\\lib-dynload', 'E:\\Work\\Dev\\StackExchange\\StackOverflow\\q073506592', 'C:\\Install\\pc032\\FontForge\\FontForgeBuilds\\Version\\lib\\python3.10\\site-packages']
    <module 'fontforge' from 'C:\\Install\\pc032\\FontForge\\FontForgeBuilds\\Version\\lib\\python3.10\\site-packages\\fontforge.pyd'>
    ['SpiroVersion', 'UnicodeAnnotationFromLib', 'UnicodeBlockCountFromLib', 'UnicodeBlockEndFromLib', 'UnicodeBlockNameFromLib', 'UnicodeBlockStartFromLib', 'UnicodeNameFromLib', 'UnicodeNames2FromLib', 'UnicodeNamesListVersion', '__date__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', '__version__', 'activeFont', 'activeFontInUI', 'activeGlyph', 'activeLayer', 'ask', 'askChoices', 'askMulti', 'askString', 'awcontext', 'awglyph', 'configurePlugins', 'contour', 'cvt', 'defaultOtherSubrs', 'font', 'fonts', 'fontsInFile', 'getConvexNib', 'getPluginInfo', 'getPrefs', 'glyph', 'glyphPen', 'hasSpiro', 'hasUserInterface', 'hooks', 'layer', 'layer_array', 'layerinfo', 'layerinfo_array', 'loadEncodingFile', 'loadNamelist', 'loadNamelistDir', 'loadPlugins', 'loadPrefs', 'logWarning', 'math', 'mathKern', 'nameFromUnicode', 'onAppClosing', 'open', 'openFilename', 'parseTTInstrs', 'point', 'postError', 'postNotice', 'preloadCidmap', 'printSetup', 'private', 'readOtherSubrsFile', 'references', '
    registerGlyphSeparationHook', 'registerImportExport', 'registerMenuItem', 'runInitScripts', 'saveFilename', 'savePrefs', 'scriptFromUnicode', 'scriptPath', 'selection', 'setConvexNib', 'setPrefs', 'spiroCorner', 'spiroG2', 'spiroG4', 'spiroLeft', 'spiroOpen', 'spiroRight', 'splineCorner', 'splineCurve', 'splineHVCurve', 'splineTangent', 'unParseTTInstrs', 'unicodeFromName', 'unitShape', 'unspecifiedMathValue', 'userConfigPath', 'version']
    
    Done.
    
    
    [prompt]>
    [prompt]> :: More tests (paths related)
    [prompt]> set _PATH=%PATH%
    
    [prompt]> set PATH=%_PATH%;c:\Install\pc032\FontForge\FontForgeBuilds\Version\bin
    
    [prompt]> :: Run FFPython (from PATH). Suppress stdout
    [prompt]> ffpython.exe ./code00.py > nul
    
    [prompt]>
    [prompt]> :: Check module
    [prompt]> "c:\Install\pc064\LucasG\DependenciesWalkerPolitistTexan\Version\DependenciesGui.exe" "c:\Install\pc032\FontForge\FontForgeBuilds\Version\lib\python3.10\site-packages\fontforge.pyd"
    
    [prompt]> :: Dependencies window pops up
    

    And the (extension) module Dependencies ([GitHub]: lucasg/Dependencies) window:

    Img0

    Apparently, everything works, and the only logical conclusion one could draw, is that your environment is messed up.
    Then I noticed a "small" detail: you're using a virtual environment.

    Output (another (Cmd) terminal):

    [cfati@CFATI-W10PC064:e:\Work\Dev\StackExchange\StackOverflow\q073506592]> sopr.bat
    ### Set shorter prompt to better fit when pasted in StackOverflow (or other) pages ###
    
    [prompt]> :: Create VEnv (from FFPython)
    [prompt]> "c:\Install\pc064\Python\Python\03.10\python.exe" -m virtualenv -p "c:\Install\pc032\FontForge\FontForgeBuilds\Version\bin\ffpython.exe" "c:\Work\Dev\VEnvs\py_pc032_03.10_test1ff0"
    created virtual environment CPython3.10.9.final.0-32 in 843ms
      creator CPython3Windows(dest=C:\Work\Dev\VEnvs\py_pc032_03.10_test1ff0, clear=False, no_vcs_ignore=False, global=False)
      seeder FromAppData(download=False, pip=bundle, setuptools=bundle, wheel=bundle, via=copy, app_data_dir=C:\Users\cfati\AppData\Local\pypa\virtualenv)
        added seed packages: pip==23.0.1, setuptools==67.4.0, wheel==0.38.4
      activators BashActivator,BatchActivator,FishActivator,NushellActivator,PowerShellActivator,PythonActivator
    
    [prompt]> "c:\Work\Dev\VEnvs\py_pc032_03.10_test1ff0\bin\activate.bat"
    
    (py_pc032_03.10_test1ff0) [prompt]>
    (py_pc032_03.10_test1ff0) [prompt]> :: Various tests
    (py_pc032_03.10_test1ff0) [prompt]> "c:\Install\pc032\FontForge\FontForgeBuilds\Version\bin\ffpython.exe" ./code00.py > nul
    
    (py_pc032_03.10_test1ff0) [prompt]> set _PATH=%PATH%
    
    (py_pc032_03.10_test1ff0) [prompt]> set PATH=%_PATH%;c:\Install\pc032\FontForge\FontForgeBuilds\Version\bin
    
    (py_pc032_03.10_test1ff0) [prompt]>
    (py_pc032_03.10_test1ff0) [prompt]> ffpython ./code00.py > nul
    
    (py_pc032_03.10_test1ff0) [prompt]> python ./code00.py > nul
    Traceback (most recent call last):
      File "e:\Work\Dev\StackExchange\StackOverflow\q073506592\code00.py", line 14, in <module>
        import fontforge as ff
    ModuleNotFoundError: No module named 'fontforge'
    
    (py_pc032_03.10_test1ff0) [prompt]>
    (py_pc032_03.10_test1ff0) [prompt]> :: View differences
    (py_pc032_03.10_test1ff0) [prompt]> ffpython ./code00.py
    Executable: C:\Install\pc032\FontForge\FontForgeBuilds\Version\bin\ffpython.exe
    Version: 3.10.9 (main, Dec 10 2022, 09:16:22)  [GCC 12.2.0 32 bit]
    CWD: E:\Work\Dev\StackExchange\StackOverflow\q073506592
    UName: None
    PATH: C:\Work\Dev\VEnvs\py_pc032_03.10_test1ff0\bin;C:\WINDOWS\System32\WindowsPowerShell\v1.0\;C:\WINDOWS\system32;C:\WINDOWS;C:\WINDOWS\System32\Wbem;C:\WINDOWS\System32\OpenSSH\;C:\Users\cfati\AppData\Local\Programs\Python\Launcher\;e:\Work\Dev\Utils\current;e:\Work\Dev\Utils\current\Win;e:\Work\Dev\VEnvs\py_pc064_03.08.07_test0\Scripts;C:\Users\cfati\AppData\Local\Microsoft\WindowsApps;;c:\Install\pc032\FontForge\FontForgeBuilds\Version\bin;C:\Install\pc032\FontForge\FontForgeBuilds\Version\bin\
    PYTHONHOME: None
    PYTHONPATH: None
    sys.path: ['e:\\Work\\Dev\\StackExchange\\StackOverflow\\q073506592', 'C:\\Install\\pc032\\FontForge\\FontForgeBuilds\\Version\\lib\\python310.zip', 'C:\\Install\\pc032\\FontForge\\FontForgeBuilds\\Version\\lib\\python3.10', 'C:\\Install\\pc032\\FontForge\\FontForgeBuilds\\Version\\lib\\python3.10\\lib-dynload', 'E:\\Work\\Dev\\StackExchange\\StackOverflow\\q073506592', 'C:\\Install\\pc032\\FontForge\\FontForgeBuilds\\Version\\lib\\python3.10\\site-packages']
    Python 3.10.9 (main, Dec 10 2022, 09:16:22)  [GCC 12.2.0 32 bit] 032bit on win32
    
    <module 'fontforge' from 'C:\\Install\\pc032\\FontForge\\FontForgeBuilds\\Version\\lib\\python3.10\\site-packages\\fontforge.pyd'>
    ['SpiroVersion', 'UnicodeAnnotationFromLib', 'UnicodeBlockCountFromLib', 'UnicodeBlockEndFromLib', 'UnicodeBlockNameFromLib', 'UnicodeBlockStartFromLib', 'UnicodeNameFromLib', 'UnicodeNames2FromLib', 'UnicodeNamesListVersion', '__date__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', '__version__', 'activeFont', 'activeFontInUI', 'activeGlyph', 'activeLayer', 'ask', 'askChoices', 'askMulti', 'askString', 'awcontext', 'awglyph', 'configurePlugins', 'contour', 'cvt', 'defaultOtherSubrs', 'font', 'fonts', 'fontsInFile', 'getConvexNib', 'getPluginInfo', 'getPrefs', 'glyph', 'glyphPen', 'hasSpiro', 'hasUserInterface', 'hooks', 'layer', 'layer_array', 'layerinfo', 'layerinfo_array', 'loadEncodingFile', 'loadNamelist', 'loadNamelistDir', 'loadPlugins', 'loadPrefs', 'logWarning', 'math', 'mathKern', 'nameFromUnicode', 'onAppClosing', 'open', 'openFilename', 'parseTTInstrs', 'point', 'postError', 'postNotice', 'preloadCidmap', 'printSetup', 'private', 'readOtherSubrsFile', 'references', '
    registerGlyphSeparationHook', 'registerImportExport', 'registerMenuItem', 'runInitScripts', 'saveFilename', 'savePrefs', 'scriptFromUnicode', 'scriptPath', 'selection', 'setConvexNib', 'setPrefs', 'spiroCorner', 'spiroG2', 'spiroG4', 'spiroLeft', 'spiroOpen', 'spiroRight', 'splineCorner', 'splineCurve', 'splineHVCurve', 'splineTangent', 'unParseTTInstrs', 'unicodeFromName', 'unitShape', 'unspecifiedMathValue', 'userConfigPath', 'version']
    
    Done.
    
    
    (py_pc032_03.10_test1ff0) [prompt]>
    (py_pc032_03.10_test1ff0) [prompt]> python ./code00.py
    Executable: C:\Work\Dev\VEnvs\py_pc032_03.10_test1ff0\bin\python.exe
    Version: 3.10.9 (main, Dec 10 2022, 09:16:22)  [GCC 12.2.0 32 bit]
    CWD: E:\Work\Dev\StackExchange\StackOverflow\q073506592
    UName: None
    PATH: C:\Work\Dev\VEnvs\py_pc032_03.10_test1ff0\bin;C:\WINDOWS\System32\WindowsPowerShell\v1.0\;C:\WINDOWS\system32;C:\WINDOWS;C:\WINDOWS\System32\Wbem;C:\WINDOWS\System32\OpenSSH\;C:\Users\cfati\AppData\Local\Programs\Python\Launcher\;e:\Work\Dev\Utils\current;e:\Work\Dev\Utils\current\Win;e:\Work\Dev\VEnvs\py_pc064_03.08.07_test0\Scripts;C:\Users\cfati\AppData\Local\Microsoft\WindowsApps;;c:\Install\pc032\FontForge\FontForgeBuilds\Version\bin;C:\Install\pc032\FontForge\FontForgeBuilds\Version\bin\
    PYTHONHOME: None
    PYTHONPATH: None
    sys.path: ['e:\\Work\\Dev\\StackExchange\\StackOverflow\\q073506592', 'C:\\Install\\pc032\\FontForge\\FontForgeBuilds\\Version\\lib\\python310.zip', 'C:\\Install\\pc032\\FontForge\\FontForgeBuilds\\Version\\lib\\python3.10', 'C:\\Install\\pc032\\FontForge\\FontForgeBuilds\\Version\\lib\\python3.10\\lib-dynload', 'E:\\Work\\Dev\\StackExchange\\StackOverflow\\q073506592', 'C:\\Work\\Dev\\VEnvs\\py_pc032_03.10_test1ff0\\lib\\python3.10\\site-packages']
    Traceback (most recent call last):
      File "e:\Work\Dev\StackExchange\StackOverflow\q073506592\code00.py", line 14, in <module>
        import fontforge as ff
    ModuleNotFoundError: No module named 'fontforge'
    

    Looking at the sys.path's last entry (in both cases), it becomes obvious why this happens.

    So, this is a virtual environment related issue. To be more precise, it's a matter of virtual environment accessing the site-packages directory of the Python instance (system) it was created from.
    In order to get past this, when creating the virtual environment, pass the --system-site-packages flag, mentioned in:

    Output (continued):

    (py_pc032_03.10_test1ff0) [prompt]> deactivate
    [prompt]>
    [prompt]> :: Create VEnv (from FFPython) - WITH --system-site-packages
    [prompt]> "c:\Install\pc064\Python\Python\03.10\python.exe" -m virtualenv -p "c:\Install\pc032\FontForge\FontForgeBuilds\Version\bin\ffpython.exe" --system-site-packages "c:\Work\Dev\VEnvs\py_pc032_03.10_test1ff1"
    created virtual environment CPython3.10.9.final.0-32 in 859ms
      creator CPython3Windows(dest=C:\Work\Dev\VEnvs\py_pc032_03.10_test1ff1, clear=False, no_vcs_ignore=False, global=True)
      seeder FromAppData(download=False, pip=bundle, setuptools=bundle, wheel=bundle, via=copy, app_data_dir=C:\Users\cfati\AppData\Local\pypa\virtualenv)
        added seed packages: pip==23.0.1, setuptools==67.4.0, wheel==0.38.4
      activators BashActivator,BatchActivator,FishActivator,NushellActivator,PowerShellActivator,PythonActivator
    
    [prompt]>
    [prompt]> "c:\Work\Dev\VEnvs\py_pc032_03.10_test1ff1\bin\activate.bat"
    
    (py_pc032_03.10_test1ff1) [prompt]>  python ./code00.py > nul
    
    (py_pc032_03.10_test1ff1) [prompt]>
    

    Important note:

    • Python bundled by FontForge doesn't seem to be compatible with other "regular" Python instances, as it's:

      • Built with GCC (as opposed to VStudio). I think a consequence of that is its folder structure resembling to a Nix one

      • Crippled: lacks some standard modules (SSL, HashLib, ..)

      so attempting to load the .pyd by one of those (or virtual environments based on them) might yield Undefined Behavior

    More debug info

    • Cmd:

      [cfati@CFATI-W10PC064:e:\Work\Dev\Utils\current\Win]> sopr.bat
      ### Set shorter prompt to better fit when pasted in StackOverflow (or other) pages ###
      
      [prompt]> echo %PATH%
      C:\WINDOWS\System32\WindowsPowerShell\v1.0\;C:\WINDOWS\system32;C:\WINDOWS;C:\WINDOWS\System32\Wbem;C:\WINDOWS\System32\OpenSSH\;C:\Users\cfati\AppData\Local\Programs\Python\Launcher\;e:\Work\Dev\Utils\current;e:\Work\Dev\Utils\current\Win;C:\Users\cfati\AppData\Local\Microsoft\WindowsApps;
      
      [prompt]>
      [prompt]> "c:\Install\pc032\FontForge\FontForgeBuilds\Version\bin\ffpython.exe" -c "import os;print(os.environ[\"PATH\"]);import fontforge;print(\"\nDone.\")"
      C:\WINDOWS\System32\WindowsPowerShell\v1.0\;C:\WINDOWS\system32;C:\WINDOWS;C:\WINDOWS\System32\Wbem;C:\WINDOWS\System32\OpenSSH\;C:\Users\cfati\AppData\Local\Programs\Python\Launcher\;e:\Work\Dev\Utils\current;e:\Work\Dev\Utils\current\Win;C:\Users\cfati\AppData\Local\Microsoft\WindowsApps;;C:\Install\pc032\FontForge\FontForgeBuilds\Version\bin\
      
      Done.
      
      [prompt]>
      [prompt]> where ffpython
      INFO: Could not find files for the given pattern(s).
      
      [prompt]> set _PATH=%PATH%
      
      [prompt]> set PATH=%_PATH%;c:\Install\pc032\FontForge\FontForgeBuilds\Version\bin
      
      [prompt]> where ffpython
      c:\Install\pc032\FontForge\FontForgeBuilds\Version\bin\ffpython.exe
      
      [prompt]>
      [prompt]> ffpython -c "import os;print(os.environ[\"PATH\"]);import fontforge;print(\"\nDone.\")"
      C:\WINDOWS\System32\WindowsPowerShell\v1.0\;C:\WINDOWS\system32;C:\WINDOWS;C:\WINDOWS\System32\Wbem;C:\WINDOWS\System32\OpenSSH\;C:\Users\cfati\AppData\Local\Programs\Python\Launcher\;e:\Work\Dev\Utils\current;e:\Work\Dev\Utils\current\Win;C:\Users\cfati\AppData\Local\Microsoft\WindowsApps;;c:\Install\pc032\FontForge\FontForgeBuilds\Version\bin;C:\Install\pc032\FontForge\FontForgeBuilds\Version\bin\
      
      Done.
      
    • PowerShell (PS):

      [cfati@CFATI-W10PC064:E:\Work\Dev\StackExchange\StackOverflow\q073506592]> function prompt { "[PS prompt]> "; }
      [PS prompt]>
      [PS prompt]> ${Env:PATH}
      C:\WINDOWS\System32\WindowsPowerShell\v1.0\;C:\WINDOWS\system32;C:\WINDOWS;C:\WINDOWS\System32\Wbem;C:\WINDOWS\System32\OpenSSH\;C:\Users\cfati\AppData\Local\Programs\Python\Launcher\;e:\Work\Dev\Utils\current;e:\Work\Dev\Utils\current\Win;C:\Users\cfati\AppData\Local\Microsoft\WindowsApps;
      [PS prompt]>
      [PS prompt]> & "c:\Install\pc032\FontForge\FontForgeBuilds\Version\bin\ffpython.exe" -c "import os;print(os.environ[\`"PATH\`"]);import fontforge;print(\`"\nDone.\`")"
      C:\WINDOWS\System32\WindowsPowerShell\v1.0\;C:\WINDOWS\system32;C:\WINDOWS;C:\WINDOWS\System32\Wbem;C:\WINDOWS\System32\OpenSSH\;C:\Users\cfati\AppData\Local\Programs\Python\Launcher\;e:\Work\Dev\Utils\current;e:\Work\Dev\Utils\current\Win;C:\Users\cfati\AppData\Local\Microsoft\WindowsApps;;C:\Install\pc032\FontForge\FontForgeBuilds\Version\bin\
      
      Done.
      [PS prompt]>
      [PS prompt]> where.exe ffpython
      INFO: Could not find files for the given pattern(s).
      [PS prompt]>
      [PS prompt]> ${Env:_PATH} = ${Env:PATH}
      [PS prompt]> ${Env:PATH} = ${Env:_PATH} + "c:\Install\pc032\FontForge\FontForgeBuilds\Version\bin"
      [PS prompt]> where.exe ffpython
      c:\Install\pc032\FontForge\FontForgeBuilds\Version\bin\ffpython.exe
      [PS prompt]>
      [PS prompt]> ffpython -c "import os;print(os.environ[\`"PATH\`"]);import fontforge;print(\`"\nDone.\`")"
      C:\WINDOWS\System32\WindowsPowerShell\v1.0\;C:\WINDOWS\system32;C:\WINDOWS;C:\WINDOWS\System32\Wbem;C:\WINDOWS\System32\OpenSSH\;C:\Users\cfati\AppData\Local\Programs\Python\Launcher\;e:\Work\Dev\Utils\current;e:\Work\Dev\Utils\current\Win;C:\Users\cfati\AppData\Local\Microsoft\WindowsApps;c:\Install\pc032\FontForge\FontForgeBuilds\Version\bin;C:\Install\pc032\FontForge\FontForgeBuilds\Version\bin\
      
      Done.
      

    As seen, it works from PS as well, with no (or very few) manual interventions. If things don't work on your side, try running:

    1. The above steps

    2. FFPython, but from its directory

    3. ffpython -c "import os, sys;os.add_dll_directory(os.path.dirname(sys.executable));import fontforge"

    Useful when debugging these kind of errors:



    Update #0

    After last findings, most likely some (older / newer version of) dependent .dlls of fontforge.pyd are located in some directories listed in %PATH%, and they are loaded before the correct versions (from FontForge's bin directory). That's a bit trickier to solve:

    1. Read the 1st URL from the list above and install Dependencies.

    2. Then you'll have to do a comparison of fontforge.pyd's dependent .dlls when (Dependencies tool is) launched from:

      1. FontForge's bin directory

      2. A different one

      Any .dlls that are in the former location should not be present (or if they are, they should have the same version) in any others (that come before it in %PATH%). You can either use the GUI version or the CmdLine one (like in the image below - FontForge's bin directory on the right side):

      Img1

      The command that I used to list dependent .dlls (obviously, you'll have to adjust the paths):

      "c:\Install\pc064\LucasG\DependenciesWalkerPolitistTexan\Version\Dependencies.exe" -modules "c:\Install\pc032\FontForge\FontForgeBuilds\Version\lib\python3.10\site-packages\fontforge.pyd" | findstr /R "\[Environment\] \[WindowsFolder\]"
      

      Another method is using ProcMon ([MS.Learn]: Process Monitor) when launching the script via FFPython, as shown in [SO]: C DLL loads in C++ program, not in python Ctypes (@MarkTolonen's answer)

    3. Once you've found the "faulty" .dlls, either:

      1. Remove their directories from %PATH%. Actually, removing might be enough in this particular case, but in order to be certain that it will work in any circumstance (other directories containing "duplicates" might be present on some machines at some point) you should set it to some minimum that (is required for running any Win application and) works for you. You can do that either (again, I'm exemplifying on my FontForge installation directory - adapt it to yours and things should be fine):

        • By creating a .bat wrapper file:

          set PATH=C:\WINDOWS\System32\WindowsPowerShell\v1.0\;C:\WINDOWS\system32;C:\WINDOWS;C:\WINDOWS\System32\Wbem;c:\Install\pc032\FontForge\FontForgeBuilds\Version\bin
          
          ffpython script.py arg0, arg1 :: ...
          
        • From script.py:

          import os
          os.environ["PATH"] = r"C:\WINDOWS\System32\WindowsPowerShell\v1.0\;C:\WINDOWS\system32;C:\WINDOWS;C:\WINDOWS\System32\Wbem;c:\Install\pc032\FontForge\FontForgeBuilds\Version\bin"
          
          import fontforge as ff
          

        This way, loading the .dll(s) happens in a sandboxed environment (in regard to %PATH%), immune to any directories capable of messing things up.

      2. Of course, there's the (currently working, but a bit inconvenient) option of launching the script from FontForge's bin directory (cd ${FONTFORGE_BIN_DIR} before launching the script)

      3. If any of the previous is not possible (for whatever reason - although I can't think of one at this point), remove (or better: rename) them (proceed with caution as applications that placed them there will (most likely) cease to work), but only use this as a last resort