Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make it easier for add-ons to supply custom braille tables #5489

Closed
wants to merge 8 commits into from

Conversation

dkager
Copy link
Collaborator

@dkager dkager commented Nov 13, 2015

Related issue: #3304

  • Make braille.TABLES a list so add-on code can append to it.
  • Detect if a table is one of the stock tables or a custom table based on its path.
  • Fall back to the US 8-dot computer braille table if a table can't be found, both for custom tables and stock tables.
  • In the GUI, show an error if a table is selected that can't be found. This is also logged.
  • In the GUI, sort translation and input tables alphabetically.

* Rename braille.TABLES to braille.tables and turn it into a list.
* Make the always-included patterns table path a constant.
…table paths from being computed on every translation or input.
…s when specifying an invalid table. Requires liblouis >= 2.6.3 (nvaccess#5137).
@dkager dkager changed the title Implement #3304 Make it easier for add-ons to supply custom braille tables Nov 13, 2015
@jcsteh
Copy link
Contributor

jcsteh commented Nov 25, 2015

I'm holding back on this for now because I'm looking at doing contracted braille input (#2439) for 2016.1 and that will require some refactoring of the way we handle tables. For that, we need to be able to quickly look up whether a table is contracted or not, so a list is not a good data structure. It'll probably become a dict internally, but I'm also considering adding an addTable function or similar which just handles the gory details, thus allowing for default arguments and making this a bit more future proof. There is still probably code I'll borrow from this, though.

@dkager
Copy link
Collaborator Author

dkager commented Nov 25, 2015

Yeah, that's pretty much what I thought when I heard the latest podcast. :)
Small status update from my end: meanwhile I'm working on a more complete en-US 8-dot computer braille table, which is currently my only real use case for this feature. I expect using a dictionary pretty much solves the problem. It'd probably still be nice to have the GUI pop up an error if you select a broken table, like it does when switching languages.

@@ -21,9 +21,10 @@

#: The directory in which liblouis braille tables are located.
TABLES_DIR = r"louis\tables"
PATTERNS_TABLE = os.path.join(TABLES_DIR, "braille-patterns.cti")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: UNICODE_BRAILLE_TABLE or something might be a better name for this. IMO, braille-patterns.cti is a terrible name. This table allows Unicode braille characters to be used anywhere to produce raw dots.

@jcsteh
Copy link
Contributor

jcsteh commented Dec 4, 2015

I think most of this code still applies, actually. The only thing that might change is the underlying data structure.

While I think it's simpler to just have the table list and data in the braille module (rather than having a separate module), I think I'd prefer to have code specific to setting the input table in the brailleInput module. Common code can still go in braille. In practical terms, this means:

  • Move inputTableList and setInputTable out of BrailleHandler and into BrailleInputHandler.
  • Move _getTablePath and _getTableList out of the BrailleHandler class; i.e. so it's at module level.

return table

def _getTableList(self, table):
return [self._getTablePath(table), PATTERNS_TABLE]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This worries me because of liblouis silliness. From the liblouis documentation about the table list (square brackets used for emphasis):

liblouis knows where to find all the tables that have been distributed with it. So you can just give a table name such as en-us-g2.ctb and liblouis will load it. You can also give a table name which includes a path. [If this is the first table in a list, all the tables in the list must be on the same path.] You can specify a path on which liblouis will look for table names by setting the environment variable LOUIS_TABLEPATH. This environment variable can contain one or more paths separated by commas. On receiving a table name liblouis first checks to see if it can be found on any of these paths. If not, it then checks to see if it can be found in the current directory, or, [if the first (or only) name in a table list, if it contains a path name, can be found on that path]. If not, it checks to see if it can be found on the path where the distributed tables have been installed.

According to this, it will ignore the path specified for any tables other than the first. So, if the first table is within an add-on, that means braille-patterns.cti won't be found. The stuff about the environment variable and distribution tables doesn't apply; I'm pretty sure you can't change the environment for a running process, only for subsequent processes, and we don't want to compile in an absolute path for distribution tables.

All of this said, if this is indeed the case, surely you would have seen this problem when testing? Maybe the documentation is actually wrong?

@dkager
Copy link
Collaborator Author

dkager commented Dec 4, 2015

nit: UNICODE_BRAILLE_TABLE or something might be a better name for this. IMO, braille-patterns.cti is a terrible name.
Good point. This table should also have extension uti (uncontracted), not cti (contracted). Many tables don't follow these semantics very well. The question is if this is important enough to suggest renaming the table upstream, e.g. to unicode-braille.uti.

Regarding mixing absolute and relative paths:
I discussed this with the liblouis guys. I'm not sure about the particulars anymore, but I believe that subtables are also tried against the distribution directory (otherwise you'd have to copy the recursive list of all tables your custom table happens to include). Of course it first tries the directory of the first table if it is an absolute path. See also liblouis/liblouis@dc97ef7.
My tests were consistent with this, but we definitely need to clear it up before using this code.
You can set the search path, though not from Python, with lou_setDataPath.

* Rename braille.PATTERNS_TABLE so it better reflects its intended purpose.
* Move code related to braille input into the brailleInput module.
@dkager
Copy link
Collaborator Author

dkager commented Dec 4, 2015

OK, refactored the code (as yet untested). This adds a few elements to the brailleInput module that could be considered as unnecessary boilerplating or as the cornerstones for future things to come. In particular, brailleInput now has a constructor and also responds to configuration profile switches.

@dkager
Copy link
Collaborator Author

dkager commented Dec 6, 2015

Urgh, that was dumb. Now it works.
One remaining issue: occasionally when changing translation tables I get an access violation error from liblouis. I can't think of anything in this patch that would cause that, other than a buggy Python wrapper for liblouis. Maybe this is a case of #4914 or #5039.

@jcsteh
Copy link
Contributor

jcsteh commented Dec 7, 2015

Good point. This table should also have extension uti (uncontracted), not cti (contracted). Many tables don't follow these semantics very well. The question is if this is important enough to suggest renaming the table upstream, e.g. to unicode-braille.uti.

I don't think it's worth it. I've made this point in the past and no one seems to care. If we do it now, we'd probably end up renaming 90% of the tables directory. :)

Regarding mixing absolute and relative paths:
I discussed this with the liblouis guys. I'm not sure about the particulars anymore, but I believe that subtables are also tried against the distribution directory

The problem is that the distribution directory isn't relevant here. It will change across instances of NVDA, so we don't/can't set it.

My tests were consistent with this, but we definitely need to clear it up before using this code.

Interesting that your tests seem to work. I wonder how it's finding the correct tables. That suggests it doesn't ignore the path if specified in subsequent tables. Did inclusion of dist tables work from custom tables?

You can set the search path, though not from Python, with lou_setDataPath.

In the end, this may be the simplest solution. That wasn't available when I originally wrote this code.

One remaining issue: occasionally when changing translation tables I get an access violation error from liblouis. I can't think of anything in this patch that would cause that, other than a buggy Python wrapper for liblouis.

More likely a bug in liblouis itself, since the wrapper just calls liblouis functions.

@dkager
Copy link
Collaborator Author

dkager commented Dec 7, 2015

I’ll do more extensive testing with the current liblouis and the t3304 branch later today to see what does and what doesn’t work. We should probably also add lou_setDataPath to the Python wrapper if that proves to be the easiest and most portable solution. I now also recall that liblouis looked in some hard-coded path, I believe “liblouis/tables”, which doesn’t work for NVDA since it uses “louis/tables”.

From: James Teh [mailto:notifications@github.com]
Sent: Monday, December 07, 2015 07:03
To: nvaccess/nvda nvda@noreply.github.com
Cc: Davy Kager mail@davykager.nl
Subject: Re: [nvda] Make it easier for add-ons to supply custom braille tables (#5489)

Good point. This table should also have extension uti (uncontracted), not cti (contracted). Many tables don't follow these semantics very well. The question is if this is important enough to suggest renaming the table upstream, e.g. to unicode-braille.uti.

I don't think it's worth it. I've made this point in the past and no one seems to care. If we do it now, we'd probably end up renaming 90% of the tables directory. :)

Regarding mixing absolute and relative paths:
I discussed this with the liblouis guys. I'm not sure about the particulars anymore, but I believe that subtables are also tried against the distribution directory

The problem is that the distribution directory isn't relevant here. It will change across instances of NVDA, so we don't/can't set it.

My tests were consistent with this, but we definitely need to clear it up before using this code.

Interesting that your tests seem to work. I wonder how it's finding the correct tables. That suggests it doesn't ignore the path if specified in subsequent tables. Did inclusion of dist tables work from custom tables?

You can set the search path, though not from Python, with lou_setDataPath.

In the end, this may be the simplest solution. That wasn't available when I originally wrote this code.

One remaining issue: occasionally when changing translation tables I get an access violation error from liblouis. I can't think of anything in this patch that would cause that, other than a buggy Python wrapper for liblouis.

More likely a bug in liblouis itself, since the wrapper just calls liblouis functions.


Reply to this email directly or view it on GitHub #5489 (comment) . https://github.com/notifications/beacon/AC9y8dLrzX6UVt4JLTYwZainqVbwUVI_ks5pNRikgaJpZM4Ghypq.gif

@dkager
Copy link
Collaborator Author

dkager commented Dec 7, 2015

Looking at the liblouis code I remember why I didn't use lou_setDataPath: it always appends "liblouis\tables". Given that NVDA uses "louis\tables" this doesn't work very well. On Linux it probably makes more sense, you'd use something like "/usr/local/share" as data path. Maybe we could suggest to have these semantics changed a bit so braille.py can safely pass table names without a path. That is:

import louis
louis.setDataPath(r"louis\tables")
louis.translate("foo.ctb", ...)

Another path liblouis tries, only on Windows, is the program path, which presumably is the path where nvda.exe (or nvda.pyw) lives. Then if you prefix "louis\tables", which is what happens in braille.py, you end up with a relative path that can be resolved. But this isn't as efficient since the program path is checked after the data path. It also doesn't explicitly say that liblouis tables are stored in "louis\tables".

@jcsteh
Copy link
Contributor

jcsteh commented Dec 7, 2015

Maybe we could suggest to have these semantics changed a bit so braille.py can safely pass table names without a path. That is:

louis.setDataPath(r"louis\tables")
louis.translate("foo.ctb", ...)

While this would certainly be nice, I fear this would be a backwards incompatible change.

Another path liblouis tries, only on Windows, is the program path, which presumably is the path where nvda.exe (or nvda.pyw) lives.

It also tries the current directory, which again is the program directory for NVDA.

Then if you prefix "louis\tables", which is what happens in braille.py, you end up with a relative path that can be resolved.

Indeed. However, this means there is still a path and the documentation suggests that the path of any table other than the first is ignored. Note that it says "path", not "absolute path", which suggests that relative paths are ignored as well.

@dkager
Copy link
Collaborator Author

dkager commented Dec 8, 2015

I’m told fixing the liblouis documentation on this topic is on the to-do list. :)

This still leaves the question if splitting the code between braille and brailleInput was an improvement. It’s probably more extensible if nothing else. Either version should work with recent versions of liblouis, though.

From: James Teh [mailto:notifications@github.com]
Sent: Monday, December 07, 2015 23:36
To: nvaccess/nvda nvda@noreply.github.com
Cc: Davy Kager mail@davykager.nl
Subject: Re: [nvda] Make it easier for add-ons to supply custom braille tables (#5489)

Maybe we could suggest to have these semantics changed a bit so braille.py can safely pass table names without a path. That is:

louis.setDataPath(r"louis\tables")
louis.translate("foo.ctb", ...)

While this would certainly be nice, I fear this would be a backwards incompatible change.

Another path liblouis tries, only on Windows, is the program path, which presumably is the path where nvda.exe (or nvda.pyw) lives.

It also tries the current directory, which again is the program directory for NVDA.

Then if you prefix "louis\tables", which is what happens in braille.py, you end up with a relative path that can be resolved.

Indeed. However, this means there is still a path and the documentation suggests that the path of any table other than the first is ignored. Note that it says "path", not "absolute path", which suggests that relative paths are ignored as well.


Reply to this email directly or view it on GitHub #5489 (comment) . https://github.com/notifications/beacon/AC9y8f_ea8pICP5kMeIqMEKMvTIXAoFBks5pNgE3gaJpZM4Ghypq.gif

@jcsteh
Copy link
Contributor

jcsteh commented Dec 8, 2015

This adds a few elements to the brailleInput module that could be considered as unnecessary boilerplating or as the cornerstones for future things to come. In particular, brailleInput now has a constructor and also responds to configuration profile switches.

Heh. I didn't think of this when I suggested it. However, it's worth noting that #2439 also requires BrailleInputHandler to have a constructor, as well as setting a variable indicating whether the table is contracted or not. Therefore, I think it still makes sense for this stuff to go in brailleInput.

@dkager
Copy link
Collaborator Author

dkager commented Dec 9, 2015

Well, that’s how it is in the topic branch now, and I also got to test it. It works!

Oh, and even tables without contractions, such as the Dutch literary braille standard, will be a lot of fun to backtranslate. Signs for capitals, numbers, emphasis… This is one ambitious task. :)

From: James Teh [mailto:notifications@github.com]
Sent: Tuesday, December 08, 2015 23:42
To: nvaccess/nvda nvda@noreply.github.com
Cc: Davy Kager mail@davykager.nl
Subject: Re: [nvda] Make it easier for add-ons to supply custom braille tables (#5489)

This adds a few elements to the brailleInput module that could be considered as unnecessary boilerplating or as the cornerstones for future things to come. In particular, brailleInput now has a constructor and also responds to configuration profile switches.

Heh. I didn't think of this when I suggested it. However, it's worth noting that #2439 #2439 also requires BrailleInputHandler to have a constructor, as well as setting a variable indicating whether the table is contracted or not. Therefore, I think it still makes sense for this stuff to go in brailleInput.


Reply to this email directly or view it on GitHub #5489 (comment) . https://github.com/notifications/beacon/AC9y8cWdG4wcmm0fKPDsBVrfxM9PnRB3ks5pN1RIgaJpZM4Ghypq.gif

@dkager dkager mentioned this pull request Jun 27, 2016
@jcsteh
Copy link
Contributor

jcsteh commented Jul 7, 2017

Now that contracted braille input (#6449) has been merged, this needs to be refactored. Sorry. :( On the flip side, it should be a lot cleaner. There is already a brailleTables.addTable function to add a new table. The only issue is that the code doesn't handle custom paths. That code from your existing branch still applies.

@dkager
Copy link
Collaborator Author

dkager commented Aug 10, 2017

Now that contracted braille input (#6449) has been merged, this needs to be refactored. Sorry. :( On the flip side, it should be a lot cleaner.

There is one change that I feel made things a lot less clean, the property and setter for brailleInput.table. Would anyone object if I replaced them with an explicit method updateTable? This makes it clear that there are going to be side effects, which the setter doesn't.

@jcsteh
Copy link
Contributor

jcsteh commented Aug 15, 2017 via email

@dkager
Copy link
Collaborator Author

dkager commented Aug 15, 2017

It updates the config, which as you said could be seen as reasonable. But I would also like to do error handling in tables and falling back to default tables in one place. For example, renamed tables in config profiles aren't handled correctly. It seems cleaner to have one function setTable that deals with renames, validates the table, falls back if necessary and finally updates the config.

@jcsteh
Copy link
Contributor

jcsteh commented Aug 15, 2017 via email

@dkager
Copy link
Collaborator Author

dkager commented Aug 15, 2017

I'm also not sure if falling back to another table should be silent or if it should pop up an error in the UI. I would go for silent, but with an error written to the log of course.

@jcsteh
Copy link
Contributor

jcsteh commented Aug 15, 2017 via email

@LeonarddeR
Copy link
Collaborator

@dkager: I'm afraid this has lots of conflicts, again. Could you provide a status update for this?

@dkager
Copy link
Collaborator Author

dkager commented Jan 17, 2018

This is too old. Closing and starting fresh when time permits.

@dkager dkager closed this Jan 17, 2018
@JulienCochuyt JulienCochuyt mentioned this pull request Sep 8, 2019
7 tasks
@LeonarddeR LeonarddeR mentioned this pull request Feb 24, 2024
14 tasks
seanbudd pushed a commit that referenced this pull request May 7, 2024
Fixes #3304.
Fixes #9863.
Supersedes PR #9864, #10172.
Addresses #505 (comment)

Summary of the issue:
In NVDA, there is no easy and reliable way for an add-on to provide a new braille table. For an experienced users wishing to do so there are two options:

Alter manually an existing table in louis/tables.
The new table still has its original name in the settings GUI.
This change is lost upon NVDA updates.

Set the absolute path to the table file in the configuration in lieu of the usual file name.
The settings GUI shows an empty entry for this one.
Forces to manually alter nvda.ini.
Forces to copy in the same directory the whole dependency chain of the new table plus braille-patterns.cti. This is because liblouis default table resolver only looks for tables in a single directory, See Make it easier for add-ons to supply custom braille tables #5489 (comment)

Description of how this pull request fixes the issue:
Add a new brailleTables optional directory in both the user scratchpad directory and the add-on directory structure.

Support reading tables metadata from an optional brailleTables section of the add-on manifest or from a manifest.ini file with the same format found in the root of the scratchpad directory, allowing a user to provide a display name and set output/input/contracted capabilities with no code.

Implement a custom liblouis table resolver that resolves tables based on what is registered in the brailleTables module:

When liblouis calls the resolver without a base file specified, the table is looked up from the brailleTables module and either resolved from the add-ons brailleTables directory or the built-in tables directory
When liblouis calls the resolver with a base file specified (e.g. when processing includes in tables), the table is looked up from the folder of the base table and/or the built-in tables directory
Enforce the existing fallback mechanism to ensure there still is braille output if the configured table cannot be found e.g. because an add-on or the scratchpad directory was disabled. This now applies both to the main configuration and individual profiles and also covers braille input.

Note that if an add-on author wants a table to be listed in the GUI, he/she should always define the table in the manifest. Contrary to earlier incarnations of this pr, replacing a table in an add-on (i.e. when it has the same filename as a built-in table) without defining it in the manifest is no longer possible. Therefore it is also not possible to replace unlisted tables that are included by listed tables. For example, if you want to replace spaces.uti as included in nl-comp8.utb, you weel need to both define and bundle a replacement of nl-comp8.utb and spaces.uti in your add-on.
XLTechie pushed a commit to XLTechie/xlnvda that referenced this pull request May 10, 2024
Fixes nvaccess#3304.
Fixes nvaccess#9863.
Supersedes PR nvaccess#9864, nvaccess#10172.
Addresses nvaccess#505 (comment)

Summary of the issue:
In NVDA, there is no easy and reliable way for an add-on to provide a new braille table. For an experienced users wishing to do so there are two options:

Alter manually an existing table in louis/tables.
The new table still has its original name in the settings GUI.
This change is lost upon NVDA updates.

Set the absolute path to the table file in the configuration in lieu of the usual file name.
The settings GUI shows an empty entry for this one.
Forces to manually alter nvda.ini.
Forces to copy in the same directory the whole dependency chain of the new table plus braille-patterns.cti. This is because liblouis default table resolver only looks for tables in a single directory, See Make it easier for add-ons to supply custom braille tables nvaccess#5489 (comment)

Description of how this pull request fixes the issue:
Add a new brailleTables optional directory in both the user scratchpad directory and the add-on directory structure.

Support reading tables metadata from an optional brailleTables section of the add-on manifest or from a manifest.ini file with the same format found in the root of the scratchpad directory, allowing a user to provide a display name and set output/input/contracted capabilities with no code.

Implement a custom liblouis table resolver that resolves tables based on what is registered in the brailleTables module:

When liblouis calls the resolver without a base file specified, the table is looked up from the brailleTables module and either resolved from the add-ons brailleTables directory or the built-in tables directory
When liblouis calls the resolver with a base file specified (e.g. when processing includes in tables), the table is looked up from the folder of the base table and/or the built-in tables directory
Enforce the existing fallback mechanism to ensure there still is braille output if the configured table cannot be found e.g. because an add-on or the scratchpad directory was disabled. This now applies both to the main configuration and individual profiles and also covers braille input.

Note that if an add-on author wants a table to be listed in the GUI, he/she should always define the table in the manifest. Contrary to earlier incarnations of this pr, replacing a table in an add-on (i.e. when it has the same filename as a built-in table) without defining it in the manifest is no longer possible. Therefore it is also not possible to replace unlisted tables that are included by listed tables. For example, if you want to replace spaces.uti as included in nl-comp8.utb, you weel need to both define and bundle a replacement of nl-comp8.utb and spaces.uti in your add-on.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants