| 44 | 44 | */ |
| 45 | 45 | private final static String R_SVG_EXPORT = |
| 46 | | "tryCatch({svg('%s')%n%s%n},finally={dev.off()})%n"; |
| 46 | "tryCatch({svg('%s'%s)%n%s%n},finally={dev.off()})%n"; |
| 47 | 47 | |
| 48 | 48 | private final static String STYLE_DIAGRAM = "diagram-"; |
| ... |
| 181 | 181 | } |
| 182 | 182 | |
| 183 | /** |
| 184 | * Evaluates an R expression. This will take into consideration any |
| 185 | * key/value pairs passed in from the document, such as width and height |
| 186 | * attributes of the form: <code>{r width=5 height=5}</code>. |
| 187 | * |
| 188 | * @param node The {@link FencedCodeBlock} to evaluate using R. |
| 189 | * @param context Used to resolve the link that refers to any resulting |
| 190 | * image produced by the R chunk (such as a plot). |
| 191 | * @return The SVG text string associated with the content produced by |
| 192 | * the chunk (such as a graphical data plot). |
| 193 | */ |
| 194 | @SuppressWarnings( "unused" ) |
| 183 | 195 | private Tuple<String, ResolvedLink> evaluateRChunk( |
| 184 | 196 | final FencedCodeBlock node, |
| 185 | 197 | final NodeRendererContext context ) { |
| 186 | 198 | final var content = node.getContentChars().normalizeEOL().trim(); |
| 187 | 199 | final var text = mRVariableProcessor.apply( content ); |
| 188 | 200 | final var hash = Integer.toHexString( text.hashCode() ); |
| 189 | 201 | final var filename = format( "%s-%s.svg", APP_TITLE_LOWERCASE, hash ); |
| 190 | 202 | final var svg = Paths.get( TEMP_DIR, filename ).toString(); |
| 191 | 203 | final var link = context.resolveLink( LINK, svg, false ); |
| 192 | | final var r = format( R_SVG_EXPORT, svg, text ); |
| 204 | final var dimensions = getAttributes( node.getInfo() ); |
| 205 | final var r = format( R_SVG_EXPORT, svg, dimensions, text ); |
| 193 | 206 | final var result = mRChunkEvaluator.apply( r ); |
| 194 | 207 | |
| 195 | 208 | return new Tuple<>( svg, link ); |
| 209 | } |
| 210 | |
| 211 | /** |
| 212 | * Splits attributes of the form <code>{r key1=value2 key2=value2}</code> |
| 213 | * into a comma-separated string containing only the key/value pairs, |
| 214 | * such as <code>key1=value1,key2=value2</code>. |
| 215 | * |
| 216 | * @param bs The complete line after the fenced block demarcation. |
| 217 | * @return A comma-separated string of name/value pairs. |
| 218 | */ |
| 219 | private String getAttributes( final BasedSequence bs ) { |
| 220 | final var result = new StringBuilder(); |
| 221 | final var split = bs.splitList( " " ); |
| 222 | final var splits = split.size(); |
| 223 | |
| 224 | for( var i = 1; i < splits; i++ ) { |
| 225 | final var based = split.get( i ).toString(); |
| 226 | final var attribute = based.replace( '}', ' ' ); |
| 227 | |
| 228 | // The order of attribute evaluations is in order of performance. |
| 229 | if( !attribute.isBlank() && |
| 230 | attribute.indexOf( '=' ) > 1 && |
| 231 | attribute.matches( ".*\\d.*" ) ) { |
| 232 | |
| 233 | // The comma will do double-duty for separating individual attributes |
| 234 | // as well as being the comma that separates all attributes from the |
| 235 | // SVG image file name. |
| 236 | result.append( ',' ).append( attribute ); |
| 237 | } |
| 238 | } |
| 239 | |
| 240 | return result.toString(); |
| 196 | 241 | } |
| 197 | 242 | |