| clue(); | ||
| - // Processing the text will update the status bar. | ||
| + // Processing the text may update the status bar. | ||
| process( getActiveTextEditor() ); | ||
| } |
| import java.util.zip.GZIPInputStream; | ||
| -import static com.keenwrite.events.StatusEvent.clue; | ||
| import static java.lang.System.getProperty; | ||
| import static java.lang.System.setProperty; | ||
| * @return The server response. | ||
| */ | ||
| - public static Response httpGet( final URL url ) throws IOException { | ||
| - return new Response( url ); | ||
| + public static Response httpGet( final URL url ) throws Exception { | ||
| + return new Response(url); | ||
| } | ||
| /** | ||
| * Convenience method to send an HTTP GET request to a server. | ||
| * | ||
| * @param uri The remote resource to fetch. | ||
| * @return The server response. | ||
| * @see #httpGet(URL) | ||
| */ | ||
| - public static Response httpGet( final URI uri ) throws IOException { | ||
| - clue( "Main.status.image.request.init" ); | ||
| + public static Response httpGet( final URI uri ) throws Exception { | ||
| return httpGet( uri.toURL() ); | ||
| } | ||
| * @see #httpGet(URL) | ||
| */ | ||
| - public static Response httpGet( final String url ) throws IOException { | ||
| + public static Response httpGet( final String url ) throws Exception { | ||
| return httpGet( new URL( url ) ); | ||
| } | ||
| private Response( final URL url ) throws IOException { | ||
| assert url != null; | ||
| + | ||
| + //clue( "Main.status.image.request.init" ); | ||
| final var connection = url.openConnection(); | ||
| mConn.setRequestProperty( "connection", "close" ); | ||
| mConn.connect(); | ||
| - clue( "Main.status.image.request.fetch", url.getHost() ); | ||
| + //clue( "Main.status.image.request.fetch", url.getHost() ); | ||
| final var code = mConn.getResponseCode(); | ||
| } | ||
| - clue( "Main.status.image.request.success", mediaType ); | ||
| + //clue( "Main.status.image.request.success", mediaType ); | ||
| return mediaType; | ||
| } | ||
| import com.keenwrite.preferences.LocaleProperty; | ||
| import com.keenwrite.preferences.Workspace; | ||
| -import javafx.application.Platform; | ||
| import javafx.beans.property.DoubleProperty; | ||
| import javafx.beans.property.StringProperty; | ||
| import java.util.Locale; | ||
| -import static com.keenwrite.constants.Constants.*; | ||
| import static com.keenwrite.Messages.get; | ||
| +import static com.keenwrite.constants.Constants.*; | ||
| import static com.keenwrite.events.Bus.register; | ||
| import static com.keenwrite.events.ScrollLockEvent.fireScrollLockEvent; | ||
| import static com.keenwrite.events.StatusEvent.clue; | ||
| import static com.keenwrite.preferences.WorkspaceKeys.*; | ||
| import static com.keenwrite.ui.fonts.IconFactory.getIconFont; | ||
| import static java.awt.BorderLayout.*; | ||
| import static java.lang.Math.max; | ||
| import static java.lang.String.format; | ||
| import static java.lang.Thread.sleep; | ||
| -import static javafx.application.Platform.runLater; | ||
| import static javafx.scene.CacheHint.SPEED; | ||
| import static javax.swing.SwingUtilities.invokeLater; | ||
| private String mHead = ""; | ||
| - private boolean mLocked; | ||
| + private volatile boolean mLocked; | ||
| private final JButton mScrollLockButton = new JButton(); | ||
| } | ||
| - final Runnable scrollToBox = () -> { | ||
| + invokeLater( () -> { | ||
| int iter = 0; | ||
| Box box = null; | ||
| scrollTo( box ); | ||
| - }; | ||
| - | ||
| - if( Platform.isFxApplicationThread() ) { | ||
| - scrollToBox.run(); | ||
| - } | ||
| - else { | ||
| - runLater( scrollToBox ); | ||
| - } | ||
| + } ); | ||
| } | ||
| private void scrollTo( final Box box ) { | ||
| if( box != null ) { | ||
| - scrollTo( createPoint( box ) ); | ||
| - } | ||
| - } | ||
| - | ||
| - private void scrollTo( final Point point ) { | ||
| - invokeLater( () -> { | ||
| - mView.scrollTo( point ); | ||
| + mView.scrollTo( createPoint( box ) ); | ||
| getScrollPane().repaint(); | ||
| - } ); | ||
| + } | ||
| } | ||
| import com.keenwrite.typesetting.Typesetter; | ||
| +import javafx.concurrent.Task; | ||
| + | ||
| +import java.nio.file.Path; | ||
| +import java.util.concurrent.ExecutorService; | ||
| import static com.keenwrite.Bootstrap.APP_TITLE_LOWERCASE; | ||
| import static com.keenwrite.Messages.get; | ||
| import static com.keenwrite.events.StatusEvent.clue; | ||
| import static com.keenwrite.io.MediaType.TEXT_XML; | ||
| import static java.nio.file.Files.writeString; | ||
| +import static java.util.concurrent.Executors.newFixedThreadPool; | ||
| /** | ||
| * Responsible for using a typesetting engine to convert an XHTML document | ||
| * into a PDF file. | ||
| */ | ||
| public final class PdfProcessor extends ExecutorProcessor<String> { | ||
| + private static final ExecutorService sExecutor = newFixedThreadPool( 5 ); | ||
| private final ProcessorContext mContext; | ||
| */ | ||
| public String apply( final String xhtml ) { | ||
| - try { | ||
| - clue( get( "Main.status.typeset.create" ) ); | ||
| - final var sTypesetter = new Typesetter( mContext.getWorkspace() ); | ||
| - final var document = TEXT_XML.createTemporaryFile( APP_TITLE_LOWERCASE ); | ||
| - final var pathOutput = mContext.getExportPath(); | ||
| + final var exporter = new Exporter( xhtml ); | ||
| + exporter.setOnRunning( e -> clue( get( "Main.status.typeset.create" ) ) ); | ||
| + exporter.setOnSucceeded( e -> { | ||
| clue( get( "Main.status.typeset.export" ) ); | ||
| - final var pathInput = writeString( document, xhtml ); | ||
| - sTypesetter.typeset( pathInput, pathOutput ); | ||
| - } catch( final Exception ex ) { | ||
| - clue( ex ); | ||
| - } | ||
| + | ||
| + final var pathOutput = mContext.getExportPath(); | ||
| + final var pathInput = exporter.getValue(); | ||
| + final var typesetter = new Typesetter( mContext.getWorkspace() ); | ||
| + | ||
| + try { | ||
| + typesetter.typeset( pathInput, pathOutput ); | ||
| + } catch( final Exception ex ) { | ||
| + clue( ex ); | ||
| + } | ||
| + } ); | ||
| + | ||
| + sExecutor.execute( exporter ); | ||
| // Do not continue processing (the document was typeset into a binary). | ||
| return null; | ||
| + } | ||
| + | ||
| + /** | ||
| + * Responsible for exporting the active document to a file. That file is | ||
| + * then read and typeset by a third-party application. | ||
| + */ | ||
| + private static class Exporter extends Task<Path> { | ||
| + private final String mXhtml; | ||
| + | ||
| + private Exporter( final String xhtml ) { | ||
| + mXhtml = xhtml; | ||
| + } | ||
| + | ||
| + @Override | ||
| + protected Path call() throws Exception { | ||
| + final var document = TEXT_XML.createTemporaryFile( APP_TITLE_LOWERCASE ); | ||
| + return writeString( document, mXhtml ); | ||
| + } | ||
| } | ||
| } | ||
| package com.keenwrite.processors; | ||
| -import com.keenwrite.io.HttpFacade; | ||
| import com.keenwrite.preferences.Workspace; | ||
| import static com.keenwrite.Bootstrap.APP_TITLE_LOWERCASE; | ||
| import static com.keenwrite.events.StatusEvent.clue; | ||
| +import static com.keenwrite.io.HttpFacade.httpGet; | ||
| import static com.keenwrite.preferences.WorkspaceKeys.KEY_IMAGES_DIR; | ||
| import static com.keenwrite.preferences.WorkspaceKeys.KEY_IMAGES_ORDER; | ||
| assert context != null; | ||
| - | ||
| mContext = context; | ||
| } | ||
| // Download remote resources into temporary files. | ||
| if( protocol.isRemote() ) { | ||
| - final var response = HttpFacade.httpGet( src); | ||
| + final var response = httpGet( src); | ||
| final var mediaType = response.getMediaType(); | ||
| import com.keenwrite.preferences.Key; | ||
| import com.keenwrite.preferences.Workspace; | ||
| +import javafx.concurrent.Task; | ||
| -import java.io.IOException; | ||
| import java.nio.file.Path; | ||
| import java.util.ArrayList; | ||
| import java.util.List; | ||
| -import java.util.concurrent.Callable; | ||
| -import java.util.concurrent.ExecutorService; | ||
| +import java.util.concurrent.TimeoutException; | ||
| -import static com.keenwrite.constants.Constants.DEFAULT_DIRECTORY; | ||
| import static com.keenwrite.Messages.get; | ||
| +import static com.keenwrite.constants.Constants.DEFAULT_DIRECTORY; | ||
| import static com.keenwrite.events.StatusEvent.clue; | ||
| import static com.keenwrite.preferences.WorkspaceKeys.KEY_TYPESET_CONTEXT_ENV; | ||
| import static com.keenwrite.preferences.WorkspaceKeys.KEY_TYPESET_CONTEXT_PATH; | ||
| +import static java.lang.Long.MAX_VALUE; | ||
| import static java.lang.String.format; | ||
| import static java.lang.System.currentTimeMillis; | ||
| import static java.util.concurrent.Executors.newFixedThreadPool; | ||
| import static java.util.concurrent.TimeUnit.*; | ||
| /** | ||
| - * Represents the executable responsible for typesetting text. This will | ||
| + * Responsible for invoking an executable to typeset text. This will | ||
| * construct suitable command-line arguments to invoke the typesetting engine. | ||
| */ | ||
| public class Typesetter { | ||
| - private static final File TYPESETTER = new File( "context" ); | ||
| - | ||
| - private static final ExecutorService sService = newFixedThreadPool( 5 ); | ||
| + private static final File TYPESETTER = new File( "mtxrun" ); | ||
| private final Workspace mWorkspace; | ||
| */ | ||
| public void typeset( final Path input, final Path output ) | ||
| - throws IOException { | ||
| + throws Exception { | ||
| if( TYPESETTER.canRun() ) { | ||
| - sService.submit( new TypesetTask( input, output ) ); | ||
| + final var executor = newFixedThreadPool( 5 ); | ||
| + final var task = new TypesetTask( input, output ); | ||
| + final var elapsed = currentTimeMillis(); | ||
| + | ||
| + task.setOnRunning( | ||
| + e -> clue( get( | ||
| + "Main.status.typeset.began", output | ||
| + ) ) | ||
| + ); | ||
| + | ||
| + task.setOnSucceeded( | ||
| + e -> clue( get( | ||
| + "Main.status.typeset.ended.success", output, since( elapsed ) | ||
| + ) ) | ||
| + ); | ||
| + | ||
| + task.setOnFailed( | ||
| + e -> clue( get( | ||
| + "Main.status.typeset.ended.failure", | ||
| + output, since( elapsed ), task.getValue() | ||
| + ) ) | ||
| + ); | ||
| + | ||
| + executor.execute( task ); | ||
| + executor.shutdown(); | ||
| + if( !executor.awaitTermination( MAX_VALUE, NANOSECONDS ) ) { | ||
| + throw new TimeoutException(); | ||
| + } | ||
| } | ||
| } | ||
| /** | ||
| * Launches a task to typeset a document. | ||
| */ | ||
| - public class TypesetTask implements Callable<Integer> { | ||
| + private class TypesetTask extends Task<Integer> { | ||
| private final List<String> mArgs = new ArrayList<>(); | ||
| /** | ||
| * Working directory must be set because ConTeXt cannot write the | ||
| * result to an arbitrary location. | ||
| */ | ||
| private final Path mDirectory; | ||
| - | ||
| - /** | ||
| - * Fully qualified destination file name. | ||
| - */ | ||
| - private final Path mOutput; | ||
| public TypesetTask( final Path input, final Path output ) { | ||
| final var filename = output.getFileName(); | ||
| final var parentDir = output.getParent(); | ||
| mDirectory = (parentDir == null ? DEFAULT_DIRECTORY : parentDir); | ||
| - mOutput = output; | ||
| final var paths = getProperty( KEY_TYPESET_CONTEXT_PATH ); | ||
| final var envs = getProperty( KEY_TYPESET_CONTEXT_ENV ); | ||
| mArgs.add( TYPESETTER.getName() ); | ||
| + mArgs.add( "--autogenerate" ); | ||
| + mArgs.add( "--script" ); | ||
| + mArgs.add( "mtx-context" ); | ||
| mArgs.add( "--batchmode" ); | ||
| mArgs.add( "--purgeall" ); | ||
| @Override | ||
| public Integer call() throws Exception { | ||
| - final var elapsed = currentTimeMillis(); | ||
| - final var output = mOutput.toString(); | ||
| - clue( get( "Main.status.typeset.began", output ) ); | ||
| - | ||
| final var builder = new ProcessBuilder( mArgs ); | ||
| builder.directory( mDirectory.toFile() ); | ||
| - // TODO: Create luatex-cache directory in system temporary directory. | ||
| -// final var env = builder.environment(); | ||
| -// env.put( "TEXMFCACHE", System.getProperty( "java.io.tmpdir" ) ); | ||
| + final var env = builder.environment(); | ||
| + env.put( "TEXMFCACHE", System.getProperty( "java.io.tmpdir" ) ); | ||
| + //final var process = builder.inheritIO().start(); | ||
| final var process = builder.start(); | ||
| process.waitFor(); | ||
| - | ||
| - final var code = process.exitValue(); | ||
| - final var time = asElapsed( currentTimeMillis() - elapsed ); | ||
| - clue( | ||
| - code == 0 | ||
| - ? get( "Main.status.typeset.ended.success", output, time ) | ||
| - : get( "Main.status.typeset.ended.failure", output, time, code ) | ||
| - ); | ||
| - | ||
| - return code; | ||
| + final int exit = process.exitValue(); | ||
| + process.destroy(); | ||
| + return exit; | ||
| } | ||
| } | ||
| private String getProperty( final Key key ) { | ||
| return mWorkspace.stringProperty( key ).get(); | ||
| + } | ||
| + | ||
| + /** | ||
| + * Calculates the time that has elapsed from the current time to the | ||
| + * given moment in time. | ||
| + * | ||
| + * @param start The starting time, which really should be before the | ||
| + * current time. | ||
| + * @return A human-readable formatted time. | ||
| + * @see #asElapsed(long) | ||
| + */ | ||
| + private static String since( final long start ) { | ||
| + return asElapsed( currentTimeMillis() - start ); | ||
| } | ||
| import static com.keenwrite.events.Bus.register; | ||
| +import static javafx.application.Platform.isFxApplicationThread; | ||
| import static javafx.application.Platform.runLater; | ||
| // Don't burden the repaint thread if there's no status bar change. | ||
| if( !getText().equals( message ) ) { | ||
| - runLater( | ||
| - () -> { | ||
| - final var s = message == null ? "" : message; | ||
| - final var i = s.indexOf( '\n' ); | ||
| - setText( s.substring( 0, i > 0 ? i : s.length() ) ); | ||
| - } | ||
| - ); | ||
| + final var s = message == null ? "" : message; | ||
| + final var i = s.indexOf( '\n' ); | ||
| + | ||
| + final Runnable update = | ||
| + () -> setText( s.substring( 0, i > 0 ? i : s.length() ) ); | ||
| + | ||
| + if( isFxApplicationThread() ) { | ||
| + update.run(); | ||
| + } | ||
| + else { | ||
| + runLater( update ); | ||
| + } | ||
| } | ||
| } | ||
| Author | DaveJarvis <email> |
|---|---|
| Date | 2021-04-06 16:34:51 GMT-0700 |
| Commit | 69b4fccaecaf80c748504edbe4094aec8dd489d1 |
| Parent | b34b0ef |
| Delta | 128 lines added, 84 lines removed, 44-line increase |