Dave Jarvis' Repositories

git clone https://repo.autonoma.ca/repo/keenwrite.git

Create luatex cache directory on first PDF run

AuthorDaveJarvis <email>
Date2021-04-10 22:49:01 GMT-0700
Commitd2fa07e33ca6946966c947ad6191a5c244a78e7b
Parent812332b
src/main/java/com/keenwrite/preferences/PreferencesController.java
*
* @return A component for editing preferences.
+ * @throws RuntimeException Could not construct the {@link PreferencesFx}
+ * object (e.g., illegal access permissions,
+ * unmapped XML resource).
*/
private PreferencesFx createPreferencesFx() {
src/main/java/com/keenwrite/typesetting/Typesetter.java
import static com.keenwrite.preferences.WorkspaceKeys.KEY_TYPESET_CONTEXT_ENV;
import static com.keenwrite.preferences.WorkspaceKeys.KEY_TYPESET_CONTEXT_PATH;
+import static java.lang.ProcessBuilder.Redirect.DISCARD;
import static java.lang.String.format;
import static java.lang.System.currentTimeMillis;
final var task = new TypesetTask( in, out );
final var time = currentTimeMillis();
- final var code = task.call();
- final var message = code == 0
- ? get( "Main.status.typeset.ended.success", out, since( time ) )
- : get( "Main.status.typeset.ended.failure", out, since( time ), code );
+ final var success = task.typeset();
- clue( message );
+ clue( get(
+ "Main.status.typeset.ended." + (success ? "success" : "failure"),
+ out, since( time ) )
+ );
}
}
/**
* Launches a task to typeset a document.
*/
- private class TypesetTask implements Callable<Integer> {
+ private class TypesetTask implements Callable<Boolean> {
private final List<String> mArgs = new ArrayList<>();
+ private final Path mInput;
+ private final Path mOutput;
/**
* Working directory must be set because ConTeXt cannot write the
* result to an arbitrary location.
*/
private final Path mDirectory;
- public TypesetTask( final Path input, final Path output ) {
- final var filename = output.getFileName();
+ private TypesetTask( final Path input, final Path output ) {
+ assert input != null;
+ assert output != null;
+
final var parentDir = output.getParent();
+ mInput = input;
+ mOutput = output;
mDirectory = (parentDir == null ? DEFAULT_DIRECTORY : parentDir);
+ }
+ /**
+ * Initializes ConTeXt, which means creating the cache directory if it
+ * doesn't already exist.
+ *
+ * @return {@code true} if the cache directory exists.
+ */
+ private boolean reinitialize() {
+ final var filename = mOutput.getFileName();
final var paths = getProperty( KEY_TYPESET_CONTEXT_PATH );
final var envs = getProperty( KEY_TYPESET_CONTEXT_ENV );
+ final var exists = getCacheDir().exists();
+ // Ensure invoking multiple times will load the correct arguments.
+ mArgs.clear();
mArgs.add( TYPESETTER.getName() );
- mArgs.add( "--autogenerate" );
- mArgs.add( "--script" );
- mArgs.add( "mtx-context" );
- mArgs.add( "--batchmode" );
- mArgs.add( "--purgeall" );
- mArgs.add( "--path='" + paths + "'" );
- mArgs.add( "--environment='" + envs + "'" );
- mArgs.add( "--result='" + filename + "'" );
- mArgs.add( input.toString() );
+
+ if( exists ) {
+ mArgs.add( "--autogenerate" );
+ mArgs.add( "--script" );
+ mArgs.add( "mtx-context" );
+ mArgs.add( "--batchmode" );
+ mArgs.add( "--purgeall" );
+ mArgs.add( "--path='" + paths + "'" );
+ mArgs.add( "--environment='" + envs + "'" );
+ mArgs.add( "--result='" + filename + "'" );
+ mArgs.add( mInput.toString() );
+ }
+ else {
+ mArgs.add( "--generate" );
+ }
+
+ return exists;
+ }
+
+ /**
+ * Setting {@code TEXMFCACHE} when run on a fresh system fails on first
+ * run. If the cache directory doesn't exist, attempt to create it, then
+ * call ConTeXt to generate the PDF.
+ *
+ * @return {@code true} if the document was typeset successfully.
+ * @throws Exception If the typesetter could not be invoked.
+ */
+ private boolean typeset() throws Exception {
+ return reinitialize() ? call() : call() && reinitialize() && call();
}
@Override
- public Integer call() throws Exception {
+ public Boolean call() throws Exception {
final var builder = new ProcessBuilder( mArgs );
builder.directory( mDirectory.toFile() );
+ final var cacheDir = getCacheDir();
final var env = builder.environment();
- env.put( "TEXMFCACHE", System.getProperty( "java.io.tmpdir" ) );
+ env.put( "TEXMFCACHE", cacheDir.toString() );
- //final var process = builder.inheritIO().start();
+ // Without redirecting (or draining) the output, the command will not
+ // terminate successfully.
+ builder.redirectOutput( DISCARD );
+ builder.redirectError( DISCARD );
+
final var process = builder.start();
process.waitFor();
- final int exit = process.exitValue();
+ final var exit = process.exitValue();
process.destroy();
- return exit;
+
+ // Exit value for a successful invocation of the typesetter. This value
+ // value is returned when creating the cache on the first run as well as
+ // creating PDFs on subsequent runs (after the cache has been created).
+ // Users don't care about exit codes, only whether the PDF was generated.
+ return exit == 0;
+ }
+
+ /**
+ * Returns the location of the cache directory.
+ *
+ * @return A fully qualified path to the location to store temporary
+ * files between typesetting runs.
+ */
+ private java.io.File getCacheDir() {
+ final var temp = System.getProperty( "java.io.tmpdir" );
+ final var cache = Path.of( temp, "luatex-cache" );
+ return cache.toFile();
}
}
src/main/java/com/keenwrite/ui/actions/ApplicationActions.java
selection.ifPresent( ( files ) -> {
- final var file = files.get(0);
+ final var file = files.get( 0 );
final var path = file.toPath();
final var document = editor.getText();
public void edit‿preferences() {
- new PreferencesController( getWorkspace() ).show();
+ try {
+ new PreferencesController( getWorkspace() ).show();
+ } catch( final Exception ex ) {
+ clue( ex );
+ }
}
private Optional<List<File>> pickFiles(
final File filename, final Options... options ) {
- final var picker = createPicker( options);
+ final var picker = createPicker( options );
picker.setInitialFilename( filename );
return picker.choose();
src/main/java/com/keenwrite/ui/explorer/FilePickerFactory.java
for( final var arg : args ) {
switch( arg ) {
- case FILE_EXPORT -> title = "Dialog.file.choose.export.title";
- case FILE_SAVE_AS -> title = "Dialog.file.choose.save.title";
+ case FILE_EXPORT -> {
+ title = "Dialog.file.choose.export.title";
+ action = CREATE;
+ }
+ case FILE_SAVE_AS -> {
+ title = "Dialog.file.choose.save.title";
+ action = CREATE;
+ }
+ case FILE_OPEN_SINGLE -> action = OPEN_EXISTING_SINGLE;
case FILE_OPEN_MULTIPLE -> action = OPEN_EXISTING_MULTIPLE;
- case FILE_OPEN_NEW -> action = CREATE;
case PERMIT_CREATE_DIRS -> mBuilder.setAllowDirectoryCreation( true );
}
src/main/resources/com/keenwrite/messages.properties
Main.status.typeset.began=Started typesetting ''{0}''
Main.status.typeset.ended.success=Finished typesetting ''{0}'' ({1} elapsed)
-Main.status.typeset.ended.failure=Failed to typeset ''{0}'' ({1} elapsed); exit code: {2}
+Main.status.typeset.ended.failure=Failed to typeset ''{0}'' ({1} elapsed)
Main.status.font.search.missing=No font name starting with ''{0}'' was found
Delta103 lines added, 29 lines removed, 74-line increase