Repository: fonttools/fonttools

Issue:
ttx option parse order
For me, the following command fails (tried to write to DejaVuSerif.ttx):

> ttx -t cmap -o - /usr/share/fonts/TTF/DejaVuSerif.ttf

While this one succeeds:

> ttx /usr/share/fonts/TTF/DejaVuSerif.ttf -t cmap -o -

I would like them both to work.


Discussion:
how does it fail?
I just tried your first ttx command and it actually works for me, i'm on macos using bash
oh it's the _second_ command `ttx path/to/font.ttf -t cmap -o -` that doesn't work, not the _first_ one (you confused the two probably).

I'm getting `ERROR: File not found: "-t"`.

Yeah, ideally both should work. Thanks for letting us know.
This has been a limitation of ttx's command line parser for a long time. I agree that it is ~pretty bad~ not ideal, and probably should be rewritten using argparse.
hm it seems that it's a limitation of Python's getopt implementation

> Note Unlike GNU getopt(), after a non-option argument, all further arguments are considered also non-options. This is similar to the way non-GNU Unix systems work.

https://docs.python.org/3/library/getopt.html#getopt.getopt

Maybe switching to a different argument parser (e.g. argparse) would fix this, but we'd have to be careful to match the existing command line behavior
i'm inclined to leave as is and document its behavior in the --help maybe. Unless someone is volunteering to port it over to argparse
looks like there's a https://docs.python.org/3/library/getopt.html#getopt.gnu_getopt which uses 

> GNU style scanning mode is used by default. This means that option and non-option arguments may be intermixed.

Maybe that works? I'll give it a try and report back
```diff
diff --git a/Lib/fontTools/ttx.py b/Lib/fontTools/ttx.py
index e7a068748..0adda52d7 100644
--- a/Lib/fontTools/ttx.py
+++ b/Lib/fontTools/ttx.py
@@ -375,7 +375,7 @@ def guessFileType(fileName):


 def parseOptions(args):
-    rawOptions, files = getopt.getopt(
+    rawOptions, files = getopt.gnu_getopt(
         args,
         "ld:o:fvqht:x:sgim:z:baey:",
         [
```

I can now do e.g. `ttx font.ttf -t cmap -o - | less` as well as `ttx -t cmap -o - font.ttf | less`

it seems to work! I wonder if this has other side effects though...
this is the source for gnu_getopts:
https://github.com/python/cpython/blob/fa359dfe07a07eb96e12b0053c5ae7089efdf9cc/Lib/getopt.py#L99-L147

i think it's safe to enable, i'll make a PR
Whoa, speedy! thanks



Patch:
diff --git a/Lib/fontTools/ttx.py b/Lib/fontTools/ttx.py
index e7a068748b..0adda52d74 100644
--- a/Lib/fontTools/ttx.py
+++ b/Lib/fontTools/ttx.py
@@ -375,7 +375,7 @@ def guessFileType(fileName):
 
 
 def parseOptions(args):
-    rawOptions, files = getopt.getopt(
+    rawOptions, files = getopt.gnu_getopt(
         args,
         "ld:o:fvqht:x:sgim:z:baey:",
         [


Test patch:
diff --git a/Tests/ttx/ttx_test.py b/Tests/ttx/ttx_test.py
index be009b8aa4..b3b3a13a82 100644
--- a/Tests/ttx/ttx_test.py
+++ b/Tests/ttx/ttx_test.py
@@ -1018,6 +1018,15 @@ def test_main_ttx_compile_stdin_to_stdout(tmp_path):
     assert outpath.is_file()
 
 
+def test_main_gnu_style_opts_and_args_intermixed(tmpdir):
+    # https://github.com/fonttools/fonttools/issues/3507
+    inpath = os.path.join("Tests", "ttx", "data", "TestTTF.ttf")
+    outpath = tmpdir.join("TestTTF.ttx")
+    args = ["-t", "cmap", inpath, "-o", str(outpath)]
+    ttx.main(args)
+    assert outpath.check(file=True)
+
+
 def test_roundtrip_DSIG_split_at_XML_parse_buffer_size(tmp_path):
     inpath = Path("Tests").joinpath(
         "ttx", "data", "roundtrip_DSIG_split_at_XML_parse_buffer_size.ttx"