| 2 | 2 | package com.keenwrite; |
| 3 | 3 | |
| 4 | import com.keenwrite.io.MediaType; | |
| 5 | import com.keenwrite.io.MediaTypeExtension; | |
| 6 | ||
| 4 | 7 | import java.io.File; |
| 5 | 8 | import java.nio.file.Path; |
| ... | ||
| 53 | 56 | |
| 54 | 57 | /** |
| 55 | * Looks up the {@link ExportFormat} based on the given format type and | |
| 56 | * subtype combination. | |
| 58 | * Looks up the {@link ExportFormat} based on the given path and subtype. | |
| 57 | 59 | * |
| 58 | * @param type The type to find. | |
| 59 | * @param subtype The subtype to find (for HTML). | |
| 60 | * @return An object that defines the export format according to the given | |
| 61 | * parameters. | |
| 62 | * @throws IllegalArgumentException Could not determine the type and | |
| 63 | * subtype combination. | |
| 60 | * @param path The type to find. | |
| 61 | * @param modifier The subtype to find (for HTML). | |
| 62 | * @return An object to control the output file format. | |
| 63 | * @throws IllegalArgumentException The type/subtype could not be found. | |
| 64 | */ | |
| 65 | public static ExportFormat valueFrom( final Path path, final String modifier ) | |
| 66 | throws IllegalArgumentException { | |
| 67 | assert path != null; | |
| 68 | ||
| 69 | return valueFrom( MediaType.valueFrom( path ), modifier ); | |
| 70 | } | |
| 71 | ||
| 72 | /** | |
| 73 | * Looks up the {@link ExportFormat} based on the given path and subtype. | |
| 74 | * | |
| 75 | * @param extension The type to find. | |
| 76 | * @param modifier The subtype to find (for HTML). | |
| 77 | * @return An object to control the output file format. | |
| 78 | * @throws IllegalArgumentException The type/subtype could not be found. | |
| 64 | 79 | */ |
| 65 | 80 | public static ExportFormat valueFrom( |
| 66 | final String type, | |
| 67 | final String subtype ) throws IllegalArgumentException { | |
| 68 | assert type != null; | |
| 69 | assert subtype != null; | |
| 81 | final String extension, final String modifier ) | |
| 82 | throws IllegalArgumentException { | |
| 83 | assert extension != null; | |
| 70 | 84 | |
| 71 | return switch( type.trim().toLowerCase() ) { | |
| 72 | case "html" -> "svg".equalsIgnoreCase( subtype.trim() ) | |
| 85 | return valueFrom( MediaTypeExtension.fromExtension( extension ), modifier ); | |
| 86 | } | |
| 87 | ||
| 88 | /** | |
| 89 | * Looks up the {@link ExportFormat} based on the given path and subtype. | |
| 90 | * | |
| 91 | * @param type The media type to find. | |
| 92 | * @param modifier The subtype to find (for HTML). | |
| 93 | * @return An object to control the output file format. | |
| 94 | * @throws IllegalArgumentException The type/subtype could not be found. | |
| 95 | */ | |
| 96 | public static ExportFormat valueFrom( | |
| 97 | final MediaType type, final String modifier ) { | |
| 98 | return switch( type ) { | |
| 99 | case TEXT_HTML, TEXT_XHTML -> "svg".equalsIgnoreCase( modifier.trim() ) | |
| 73 | 100 | ? HTML_TEX_SVG |
| 74 | 101 | : HTML_TEX_DELIMITED; |
| 75 | case "md" -> MARKDOWN_PLAIN; | |
| 76 | case "pdf" -> APPLICATION_PDF; | |
| 102 | case TEXT_MARKDOWN -> MARKDOWN_PLAIN; | |
| 103 | case APP_PDF -> APPLICATION_PDF; | |
| 77 | 104 | default -> throw new IllegalArgumentException( format( |
| 78 | "Unrecognized format type and subtype: '%s' and '%s'", type, subtype | |
| 105 | "Unrecognized format type and subtype: '%s' and '%s'", type, modifier | |
| 79 | 106 | ) ); |
| 80 | 107 | }; |
| 750 | 750 | .collect( |
| 751 | 751 | groupingBy( |
| 752 | path -> bin.apply( MediaType.fromFilename( path ) ), | |
| 752 | path -> bin.apply( MediaType.valueFrom( path ) ), | |
| 753 | 753 | () -> new TreeMap<>( Enum::compareTo ), |
| 754 | 754 | Collectors.toList() |
| 77 | 77 | |
| 78 | 78 | @CommandLine.Option( |
| 79 | names = {"--format-type"}, | |
| 80 | description = | |
| 81 | "Export type: html, md, pdf, xml (${DEFAULT-VALUE})", | |
| 82 | paramLabel = "String", | |
| 83 | defaultValue = "pdf", | |
| 84 | required = true | |
| 85 | ) | |
| 86 | private String mFormatType; | |
| 87 | ||
| 88 | @CommandLine.Option( | |
| 89 | names = {"--format-subtype-tex"}, | |
| 79 | names = {"--format-subtype"}, | |
| 90 | 80 | description = |
| 91 | "Export subtype for HTML formats: svg, delimited", | |
| 92 | defaultValue = "", | |
| 81 | "Export TeX subtype for HTML formats: svg, delimited", | |
| 82 | defaultValue = "svg", | |
| 93 | 83 | paramLabel = "String" |
| 94 | 84 | ) |
| ... | ||
| 215 | 205 | throws IOException { |
| 216 | 206 | final var definitions = parse( mPathVariables ); |
| 217 | final var format = ExportFormat.valueFrom( mFormatType, mFormatSubtype ); | |
| 207 | final var format = ExportFormat.valueFrom( mPathOutput, mFormatSubtype ); | |
| 218 | 208 | final var locale = lookupLocale( mLocale ); |
| 219 | 209 | final var rScript = read( mRScriptPath ); |
| ... | ||
| 263 | 253 | |
| 264 | 254 | private static String read( final Path path ) throws IOException { |
| 265 | return Files.readString( path ); | |
| 255 | return path == null ? "" : Files.readString( path ); | |
| 266 | 256 | } |
| 267 | 257 | |
| 4 | 4 | import java.io.File; |
| 5 | 5 | import java.io.IOException; |
| 6 | import java.net.URL; | |
| 6 | 7 | import java.nio.file.Path; |
| 7 | 8 | |
| 8 | 9 | import static com.keenwrite.io.MediaType.TypeName.*; |
| 9 | import static com.keenwrite.io.MediaTypeExtension.getMediaType; | |
| 10 | import static com.keenwrite.io.MediaTypeExtension.fromExtension; | |
| 10 | 11 | import static java.io.File.createTempFile; |
| 11 | 12 | import static org.apache.commons.io.FilenameUtils.getExtension; |
| ... | ||
| 184 | 185 | * @return {@link MediaType#UNDEFINED} if the extension has not been |
| 185 | 186 | * assigned, otherwise the {@link MediaType} associated with this |
| 186 | * URL's file name extension. | |
| 187 | * {@link URL}'s file name extension. | |
| 187 | 188 | */ |
| 188 | 189 | public static MediaType fromFilename( final String filename ) { |
| 189 | 190 | assert filename != null; |
| 190 | return getMediaType( getExtension( filename ) ); | |
| 191 | return fromExtension( getExtension( filename ) ); | |
| 191 | 192 | } |
| 192 | 193 | |
| 47 | 47 | MEDIA_TEXT_R_MARKDOWN( TEXT_R_MARKDOWN, of( "Rmd" ) ), |
| 48 | 48 | MEDIA_TEXT_PROPERTIES( TEXT_PROPERTIES, of( "properties" ) ), |
| 49 | MEDIA_TEXT_XHTML( TEXT_XHTML, of( "xhtml" ) ), | |
| 49 | MEDIA_TEXT_XHTML( TEXT_XHTML, of( "html", "xhtml" ) ), | |
| 50 | 50 | MEDIA_TEXT_XML( TEXT_XML ), |
| 51 | 51 | MEDIA_TEXT_YAML( TEXT_YAML, of( "yaml", "yml" ) ), |
| ... | ||
| 76 | 76 | * @return The associated {@link MediaType} as defined by IANA. |
| 77 | 77 | */ |
| 78 | static MediaType getMediaType( final String extension ) { | |
| 78 | public static MediaType fromExtension( final String extension ) { | |
| 79 | 79 | final var sanitized = sanitize( extension ); |
| 80 | 80 | |
| 25 | 25 | sSkins.add( "Haunted Grey" ); |
| 26 | 26 | sSkins.add( "Modena Dark" ); |
| 27 | sSkins.add( "Monokai" ); | |
| 27 | 28 | sSkins.add( SKIN_DEFAULT ); |
| 28 | 29 | sSkins.add( "Silver Cavern" ); |
| 68 | 68 | final Processor<String> processor; |
| 69 | 69 | |
| 70 | if( preview == null && outputType == MARKDOWN_PLAIN ) { | |
| 71 | processor = inputType == RMARKDOWN | |
| 72 | ? createInlineRProcessor( successor, context ) | |
| 73 | : createMarkdownProcessor( successor, context ); | |
| 70 | // When there's no preview, determine processor by file name extension. | |
| 71 | if( preview == null ) { | |
| 72 | if( outputType == MARKDOWN_PLAIN ) { | |
| 73 | processor = inputType == RMARKDOWN | |
| 74 | ? createInlineRProcessor( successor, context ) | |
| 75 | : createVariableProcessor( successor, context ); | |
| 76 | } | |
| 77 | else { | |
| 78 | processor = createMarkdownProcessor( successor, context ); | |
| 79 | } | |
| 74 | 80 | } |
| 75 | 81 | else { |
| 1 | /* | |
| 2 | * Theme contributed by mery6299 | |
| 3 | * | |
| 4 | * https://github.com/mery6299 | |
| 5 | */ | |
| 6 | .root { | |
| 7 | -fx-accent: #75715E; | |
| 8 | -fx-focus-color: -fx-accent; | |
| 9 | -fx-base: #262626; | |
| 10 | -fx-control-inner-background: -fx-base; | |
| 11 | -fx-control-inner-background-alt: -fx-control-inner-background; | |
| 12 | ||
| 13 | -theme-text-selection: #78dce8; | |
| 14 | -theme-search-selection: #ffd866; | |
| 15 | ||
| 16 | -fx-light-text-color: derive( -fx-base, 150% ); | |
| 17 | -fx-mid-text-color: derive( -fx-base, 100% ); | |
| 18 | -fx-dark-text-color: derive( -fx-base, 25% ); | |
| 19 | -fx-text-foreground: -fx-light-text-color; | |
| 20 | -fx-text-background: derive( -fx-control-inner-background, 25% ); | |
| 21 | -fx-text-selection: derive( -theme-text-selection, -50% ); | |
| 22 | } | |
| 23 | ||
| 24 | /* Caret colour */ | |
| 25 | .styled-text-area .caret { | |
| 26 | -fx-stroke: white; | |
| 27 | } | |
| 28 | ||
| 29 | /* Spelling errors */ | |
| 30 | .markdown .spelling { | |
| 31 | -rtfx-underline-color: #fc9867; | |
| 32 | } | |
| 33 | ||
| 34 | /* Search result */ | |
| 35 | .markdown .search { | |
| 36 | -rtfx-background-color: derive( -theme-search-selection, -25% ); | |
| 37 | } | |
| 38 | ||
| 39 | .glyph-icon { | |
| 40 | -fx-text-fill: -fx-light-text-color; | |
| 41 | -fx-fill: -fx-light-text-color; | |
| 42 | } | |
| 43 | ||
| 44 | .glyph-icon:hover { | |
| 45 | -fx-effect: dropshadow( three-pass-box, rgba( 0, 0, 0, 0.2 ), 4, 0, 0, 0 ); | |
| 46 | } | |
| 47 | ||
| 48 | .label { | |
| 49 | -fx-text-fill: -fx-light-text-color; | |
| 50 | } | |
| 51 | ||
| 52 | .text-field { | |
| 53 | -fx-prompt-text-fill: gray; | |
| 54 | } | |
| 55 | ||
| 56 | .button { | |
| 57 | -fx-focus-traversable: false; | |
| 58 | } | |
| 59 | ||
| 60 | .button:hover { | |
| 61 | -fx-text-fill: white; | |
| 62 | } | |
| 63 | ||
| 64 | .separator *.line { | |
| 65 | -fx-background-color: #3C3C3C; | |
| 66 | -fx-border-style: solid; | |
| 67 | -fx-border-width: 1px; | |
| 68 | } | |
| 69 | ||
| 70 | .scroll-bar { | |
| 71 | -fx-background-color: derive( -fx-base, 15% ); | |
| 72 | } | |
| 73 | ||
| 74 | .button:default { | |
| 75 | -fx-base: derive( -fx-accent, -25% ); | |
| 76 | } | |
| 77 | ||
| 78 | .table-view { | |
| 79 | -fx-selection-bar-non-focused: derive( -fx-base, 50% ); | |
| 80 | } | |
| 81 | ||
| 82 | .table-view .column-header .label { | |
| 83 | -fx-alignment: CENTER_LEFT; | |
| 84 | -fx-font-weight: none; | |
| 85 | } | |
| 86 | ||
| 87 | .list-cell:even, | |
| 88 | .list-cell:odd, | |
| 89 | .table-row-cell:even, | |
| 90 | .table-row-cell:odd { | |
| 91 | -fx-control-inner-background: derive( -fx-base, 15% ); | |
| 92 | } | |
| 93 | ||
| 94 | .list-cell:empty, | |
| 95 | .table-row-cell:empty { | |
| 96 | -fx-background-color: transparent; | |
| 97 | } | |
| 98 | ||
| 99 | .list-cell, | |
| 100 | .table-row-cell { | |
| 101 | -fx-border-color: transparent; | |
| 102 | -fx-table-cell-border-color: transparent; | |
| 103 | } | |
| 104 | ||
| 105 | /* Avoid clipping text descenders in statistics table row. */ | |
| 106 | .table-row-cell { | |
| 107 | -fx-cell-size: 30px; | |
| 108 | } | |
| 109 | ||
| 110 | /* Toolbar */ | |
| 111 | .tool-bar .button:hover { | |
| 112 | -fx-background-color: derive( -fx-accent, -25% ); | |
| 113 | -fx-color: -fx-hover-base; | |
| 114 | } | |
| 115 | ||
| 116 | /* Tabs */ | |
| 117 | .tab-pane *.tab-header-background { | |
| 118 | -fx-background-color: -fx-base; | |
| 119 | } | |
| 120 | ||
| 121 | .tab:selected { | |
| 122 | -fx-background-color: derive( #A9DC76, -30% ); | |
| 123 | } | |
| 124 | ||
| 1 | 125 |