I would like to use my own pdf() plot device in an .Rnw document converted to a PDF with knitr. After the PDF of a figure is generated
it should call pdfCrop.off() instead of dev.off() (or whatever knitr calls); this would
perfectly crop the resulting figures. How can this be done?
The following MWE works (but without cropping) if (*) is commented out (and the line before properly closed).
\documentclass{article}
\begin{document}
<<knitr_options, echo = FALSE, results = "hide", purl = FALSE>>=
## Custom graphics device (for cropping .pdf):
pdfCrop <- function(file, width, height, ...)
{
f <- file
grDevices::pdf(f, width = width, height = height, onefile = FALSE)
assign(".pdfCrop.file", f, envir = globalenv())
}
pdfCrop.off <- function() # used automagically
{
grDevices::dev.off() # closing the pdf device
f <- get(".pdfCrop.file", envir = globalenv())
system(paste("pdfcrop --pdftexcmd pdftex", f, f, "1>/dev/null 2>&1"),
intern = FALSE) # crop the file (relies on PATH)
}
## knitr options
knitr::opts_chunk$set(fig.path = "./fig_", background = "#FFFFFF",
dev = "pdfCrop", fig.ext = "pdf") # (*) => how to use pdfCrop.off() instead of dev.off()?
#
<<MWE>>=
<<fig-MWE, eval = FALSE, echo = FALSE>>=
plot(1:10, 10:1)
#
\setkeys{Gin}{width=\textwidth}
\begin{figure}[htbp]
\centering
\framebox{
<<figMWE, echo = FALSE, fig.width=6, fig.height=6>>=
<<fig-MWE>>
#
}
\caption{Just some text to show the actual textwidth in order to see that the
figure is not perfectly horizontally aligned due to some white space which can
be avoided by properly crop the figure with an adequate pdf crop device.}
\end{figure}
\end{document}
knitr already provides a crop device based on pdfcrop, so we can use that via a hook:
\documentclass{article}
\begin{document}
<<knitr_options, echo = FALSE, results = "hide", purl = FALSE>>=
## knitr options
library(knitr)
knit_hooks$set(crop = hook_pdfcrop)
knitr::opts_chunk$set(fig.path = "./fig_", # all figures are saved as fig_*
background = "#FFFFFF", # avoid color
crop = TRUE) # always crop
#
<<MWE>>=
<<fig-MWE, eval = FALSE, echo = FALSE>>=
plot(1:10, 10:1)
#
\setkeys{Gin}{width=\textwidth}
\begin{figure}[htbp]
\centering
<<figMWE, echo = FALSE, fig.width=6, fig.height=6>>=
<<fig-MWE>>
#
\caption{Just some text to show the actual textwidth in order to see that the
figure is not perfectly horizontally aligned due to some white space which can
be avoided by properly crop the figure with an adequate pdf crop device.}
\end{figure}
\end{document}
Related
Could you please help me in finding the right code to print a png image inside a R flexdashboard?
Here my code chunk:
---
title: "Analyse des installations PV - Les Vergers"
author: "Dario Santandrea"
date: "`r format(Sys.time(), '%d %B, %Y')`"
output:
flexdashboard::flex_dashboard:
orientation: rows
vertical_layout: fill
runtime: shiny
---
First attempt
renderImage({
outfile <- tempfile(fileext = "X:/LesVergers/Analyse/BilanSolaire/Figures/Solar_angle_scheme.png")
png(outfile, width = 500, height = 400)
dev.off()
}
)
Second attempt
#knitr::include_graphics("X:/LesVergers/Analyse/BilanSolaire/Figures/Solar_angle_scheme.png")
The image i've been trying to print doesn't simply appear in the flexdashboard
flexdashboard is essentially Markdown. Thus see e.g. this related thread.
```{r picture, echo = F, fig.cap = "Title", out.width = '100%'}
knitr::include_graphics("picture.png")
```
Just place this chunk within the standard flexdashboard layout:
BLOCK1
=====================================
Row {data-width=1000}
-----------------------------------------------------------------------
### A picture
```{r picture, echo = F, fig.cap = "Title", out.width = '100%'}
knitr::include_graphics("picture.png")
```
For example, I generate the data with:
i=0; while [ "$i" -lt 10 ]; do echo "$i,$((2*i)),$((4*i))"; i=$((i+1)); done > main.csv
which contains:
0,0,0
1,2,4
2,4,8
3,6,12
4,8,16
5,10,20
6,12,24
7,14,28
8,16,32
9,18,36
Then for example in gnuplot, I get what I want with palette:
#!/usr/bin/env gnuplot
set terminal png size 1024,1024
set output "main.png"
set datafile separator ","
set key off
plot "main.csv" using 1:2:3 palette pt 7 pointsize 10
which gives the desired:
How to achieve this effect with Paraview?
I managed to make the scatter plot with a Line Chart View, but all points are red like this:
Also I could not resize the marker sizes, but for that I found an open issue: https://gitlab.kitware.com/paraview/paraview/issues/14169
I am initially learning the GUI for plotting, but if you have a scripting option that is good to know too.
The reason I am looking into Parasol is that I need to plot 10M points interactively, which I have found gnuplot and matplotlib not to handle well, so I'm curious if this VTK-based solution will cut it. More info at: Large plot: ~20 million samples, gigabytes of data
Tested in Ubuntu 18.10, Paraview 5.4.1.
Here is the python script to read the file and display the markers in the ParaView 3D Render View.
import paraview.simple as pvs
# create a new 'CSV Reader'
csvReader = pvs.CSVReader(FileName=['C:\\your_file.csv'])
csvReader.HaveHeaders = 0
# create a new 'Table To Points'
tableToPoints = pvs.TableToPoints(Input=csvReader)
tableToPoints.XColumn = 'Field 0'
tableToPoints.YColumn = 'Field 1'
tableToPoints.ZColumn = 'Field 2'
tableToPoints.KeepAllDataArrays = 1
tableToPoints.a2DPoints = 1
# create a new 'Glyph'
glyph = pvs.Glyph(Input=tableToPoints, GlyphType='Arrow')
glyph.Scalars = ['POINTS', 'Field 0']
glyph.Vectors = ['POINTS', 'None']
glyph.GlyphType = '2D Glyph'
glyph.GlyphType.GlyphType = 'Square'
glyph.GlyphType.Filled = 1
glyph.ScaleMode = 'off'
glyph.ScaleFactor = 1.0
glyph.GlyphMode = 'All Points'
### uncomment to scale markers by 'Field 2'
# glyph1.Scalars = ['POINTS', 'Field 2']
# glyph1.ScaleMode = 'scalar'
# show data in view
renderView = pvs.GetActiveView()
glyphDisplay = pvs.Show(glyph, renderView)
glyphDisplay.Representation = 'Surface'
pvs.ColorBy(glyphDisplay, ('POINTS', 'Field 2'))
glyphDisplay.SetScalarBarVisibility(renderView, True)
glyphDisplay.RescaleTransferFunctionToDataRange(True, False)
renderView.Update()
pvs.UpdatePipeline()
renderView.AxesGrid.Visibility = 1
renderView.ResetCamera()
pvs.Render(renderView)
And the result:
I have an application that creates very nice data plots rendered in PostScript with letter size and landscape mode. An example of the input file is at http://febo.com/uploads/blip.ps. [ Note: this image renders properly in a viewer, but the PNG conversion comes out with the image sideways. ] I need to convert these PostScript files into PNG images that are scaled down and rotated 90 degrees for web presentation.
I want to do this with ghostscript and no other external tool, because the conversion program will be used on both Windows and Linux systems and gs seems to be a common denominator. (I'm creating a perl script with a "PS2png" function that will call gs, but I don't think that's relevant to the question.)
I've searched the web and spent a couple of days trying to modify examples I've found, but nothing I have tried does the combination of (a) rotate, (b) resize, (c) maintain the aspect ratio and (d) avoid clipping.
I did find an example that injects a "scale" command into the postscript stream, and that seems to work well to scale the image to the desired size while maintaining the aspect ratio. But I can't find a way to rotate the resized image so that the, e.g., 601 x 792 point (2504 x 3300 pixel) postscript input becomes an 800 x 608 pixel png output.
I'm looking for the ghostscript/postscript fu that I can pass to the gs command line to accomplish this.
I've tried gs command lines with various combinations of -dFIXEDMEDIA, -dFitPage, -dAutoRotatePages=/None, or /All, -c "<> setpagedevice", changing -dDISPLAYWIDTHPOINTS and -dDISPLAYHEIGHTPOINTS, -g[width]x[height], -dUseCropBox with rotated coordinates, and other things I've forgotten. None of those worked, though it wouldn't surprise me if there's a magic combination of some of them that will. I just haven't been able to find it.
Here is the core code that produces the scaled but not rotated output:
## "$molps" is the input ps file read to a variable
## insert the PS "scale" command
$molps = $sf . " " . $sf . " scale\n" . $molps;
$gsopt1 = " -r300 -dGraphicsAlphaBits=4 -dTextAlphaBits=4";
$gsopt1 = $gsopt1 . " -dDEVICEWIDTHPOINTS=$device_width_points";
$gsopt1 = $gsopt1 . " -dDEVICEHEIGHTPOINTS=$device_height_points";
$gsopt1 = $gsopt1 . " -sOutputFile=" . $outfile;
$gscmd = "gs -q -sDEVICE=pnggray -dNOPAUSE -dBATCH " . $gsopt1 . " - ";
system("echo \"$molps\" \| $gscmd");
$device_width_points and $device_height_points are calculated by taking the original image size and applying the scaling factor $sf.
I'll be grateful to anyone who can show me the way to accomplish this. Thanks!
Better Answer:
You almost had it with your initial research. Just set orientation in the gs call:
... | gs ... -dAutoRotatePages=/None -c '<</Orientation 3>> setpagedevice' ...
cf. discussion of setpagedevice in the Red Book, and ghostscript docs (just before section 6.2)
Original Answer:
As well as "scale", you need "rotate" and "translate", not necessarily in that order.
Presumably these are single-page PostScript files?
If you know the bounding box of the Postscript, and the dimensions of the png, it is not too arduous to calculate the necessary transformation. It'll be about one line of code. You just need to ensure you inject it in the correct place.
Chapter 6 of the Blue Book has lots of details
A ubc.ca paper provides some illustrated examples (skip to page 4)
Simple PostScript file to play around with. You'll just need the three translate,scale,rotate commands in some order. The rest is for demonstrating what's going on.
%!
% function to define a 400x400 box outline, origin at 0,0 (bottom left)
/box { 0 0 moveto 0 400 lineto 400 400 lineto 400 0 lineto closepath } def
box clip % pretend the box is our bounding box
clippath stroke % draw initial black bounding box
(Helvetica) findfont 50 scalefont setfont % setup a font
% draw box, and show some text # 100,100
box stroke
100 100 moveto (original) show
% try out some transforms
1 0 0 setrgbcolor % red
.5 .5 scale
box stroke
100 100 moveto (+scaled) show
0 1 0 setrgbcolor % green
300 100 translate
box stroke
100 100 moveto (+translated) show
0 0 1 setrgbcolor % blue
45 rotate
box stroke
100 100 moveto (+rotated) show
showpage
It may be possible to insert the calculated transformation into the gs commandline like this:
... | gs ... -c '1 2 scale 3 4 translate 5 6 rotate' -# ...
Thanks to JHNC, I think I have it licked now, and for the benefit of posterity, here's what worked. (Please upvote JHNC, not this answer.)
## code to determine original size, scaling factor, rotation goes above
my $device_width_points;
my $device_height_points;
my $orientation;
if ($rotation) {
$orientation = 3;
$device_width_points = $ytotal_png_pt;
$device_height_points = $xtotal_png_pt;
} else {
$orientation = 0;
$device_width_points = $xtotal_png_pt;
$device_height_points = $ytotal_png_pt;
}
my $orientation_string =
" -dAutoRotatePages=/None -c '<</Orientation " .
$orientation . ">> setpagedevice'";
## $ps = .ps file read into variable
## insert the PS "scale" command
$ps = $sf . " " . $sf . " scale\n" . $ps;
$gsopt1 = " -r300 -dGraphicsAlphaBits=4 -dTextAlphaBits=4";
$gsopt1 = $gsopt1 . " -dDEVICEWIDTHPOINTS=$device_width_points";
$gsopt1 = $gsopt1 . " -dDEVICEHEIGHTPOINTS=$device_height_points";
$gsopt1 = $gsopt1 . " -sOutputFile=" . $outfile;
$gsopt1 = $gsopt1 . $orientation_string;
$gscmd = "gs -q -sDEVICE=pnggray -dNOPAUSE -dBATCH " . $gsopt1 . " - ";
system("echo \"$ps\" \| $gscmd");
One of the problems I had was that some options apparently don't play well together -- for example, I tried using the -g option to set the output size in pixels, but in that case the rotation didn't work. Using the DEVICE...POINTS commands instead did work.
I have the following code in an rmd file which leverages tikz for diagrams:
---
title: "TestNonTufteLua"
author: "Me"
output:
pdf_document :
latex_engine: lualatex
---
Prove tikz works:
```{r tikTest1, engine = "tikz"}
\usetikzlibrary{shapes}
\begin{tikzpicture}
\node[ellipse, draw=black, align = center] (Data) {Data $y_{n}$};
\end{tikzpicture}
```
Then, when you set `eval = TRUE` in the below code, it will not work.
```{r tikTest2, eval = FALSE, engine = "tikz"}
\usetikzlibrary{graphs, graphdrawing}
\usegdlibrary{layered}
\tikz [gr/.style={gray!50}, font=\bfseries]
\graph [layered layout] {
% A and F are horizontally aligned if you also set weight=0.5 for A -- C.
A -- [minimum layers=2] C -- F,
{ [nodes=gr, edges=gr] A -- B -- { E, D -- F } }
};
```
When changing to eval=TRUE in the second chunk, I get the following
error:
Quitting from lines 24-29 (testNonTufteLua.Rmd) Error: running
'texi2dvi' on '.\tikz36747a021b22.tex' failed
LaTeX errors: rarygraphdrawing.code.tex:22: Package pgf Error: You
need to run LuaTeX to use the graph drawing library.
This error occurs when using the knit button from RStudio or using render("testNonTufteLua.Rmd", output_format = pdf_document(keep_tex = TRUE, latex_engine = "lualatex"). I have also experimented with setting options(tikzDefaultEngine = "luatex") to get tikzDevice to handle it properly, but it still does not work. I just can't seem to get the graphdrawing library to work even though the tikz-shapes library can be loaded and also that the rest of the document seems to be compiled with lualatex. Thanks for any help!!
Update: Meanwhile knitr no longer uses tools::texi2dvi but tinytex::latexmk. One therefore has to use options(tinytex.engine = 'lualatex') in a set-up chunk.
This is rather tricky, since you are not using tikzDevice but the tikz engine, which uses tools::texi2dvi to convert to PDF. You can change this using options(texi2dvi = "lualatex"). However, the default template does not work with LuaLaTeX. I have therefore created a modified template:
\RequirePackage{luatex85}
\documentclass{article}
\usepackage[luatex,active,tightpage]{preview}
\usepackage{amsmath}
\usepackage{tikz}
\usetikzlibrary{matrix}
\begin{document}
\begin{preview}
%% TIKZ_CODE %%
\end{preview}
\end{document}
And specify that file with engine.opts = list(template = "tikz2pdf.tex"). Putting it all together here my working file:
---
title: "TestNonTufteLua"
author: "Me"
output:
pdf_document :
latex_engine: lualatex
---
```{r}
options(texi2dvi = "lualatex")
```
```{r tikTest2, eval = TRUE, engine = "tikz", engine.opts = list(template = "tikz2pdf.tex")}
\usetikzlibrary{graphs, graphdrawing}
\usegdlibrary{layered}
\tikz [gr/.style={gray!50}, font=\bfseries]
\graph [layered layout] {
% A and F are horizontally aligned if you also set weight=0.5 for A -- C.
A -- [minimum layers=2] C -- F,
{ [nodes=gr, edges=gr] A -- B -- { E, D -- F } }
};
```
Result:
References:
how to set engine options
preview and LuaLaTeX
knitr using texi2pdf
A small running variation of the example above is the following using tinytex.
---
title: "lualatex. Using `tinytex.engine`"
output:
html_document:
df_print: paged
pdf_document:
latex_engine: lualatex
---
## Latex engines
By default, PDF documents are rendered using `pdflatex`. You can specify an
alternate engine using the `latex_engine` option. Available engines
are `pdflatex`, `xelatex`, and `lualatex.`
```{r setup}
options(tinytex.engine = "lualatex")
```
```{r tikzLua, eval = TRUE, engine = "tikz", engine.opts = list(template = "tikz2pdf.tex")}
\usetikzlibrary{graphs, graphdrawing}
\usegdlibrary{layered}
\tikz [gr/.style={gray!50}, font=\bfseries]
\graph [layered layout] {
% A and F are horizontally aligned if you also set weight=0.5 for A -- C.
A -- [minimum layers=2] C -- F,
{ [nodes=gr, edges=gr] A -- B -- { E, D -- F } }
};
```
After an update in knitr the example above stopped running.
I need to produce a large number of Powerpoint files with varying text (program of conference sessions). I try to do this with Perl and Win32::OLE. This works well with the exception of setting the color of the text I post. All I can set is the value for Red in RGB, but not the other colors. I use Powerpoint 2010. Also, I can change the color in Powerpoint via VBA.
Here is the code I use (commented with #- are some options that I tried and that did not work).
use strict;
use Win32::OLE qw(in with);
use Win32::OLE::Const 'Microsoft PowerPoint';
$Win32::OLE::Warn = 2; # Throw Errors, I'll catch them
my $PptApp = Win32::OLE->GetActiveObject('PowerPoint.Application')|| Win32::OLE->new('PowerPoint.Application', 'Quit');
$PptApp->{Visible} = 1;
my $Presentation = $PptApp->Presentations->Open({FileName=>'<input-filename.ppt>',ReadOnly=>1});
my $Slide = $Presentation->Slides(1);
$Slide->{Name} = "Slide1";
my $TextBox=$Slide->Shapes->AddTextbox({Orientation=>1,
Left=>25,
Top=>25,
Width=>550,
Height=>50,
});
$TextBox->TextFrame->TextRange->{Text} ="Big Ole Test";
$TextBox->TextFrame->TextRange->Font->{Color} = 255;
#- $TextBox->TextFrame->TextRange->Font->{Color} => ({RGB=>(Red=>86, Green=>55, Blue=>201)});
## Black
#- $TextBox->TextFrame->TextRange->Font->Color->RGB=>[255,255,255];
## Black
#- $TextBox->TextFrame->TextRange->Font->Color => [255,255,255];
## Black
#- $TextBox->TextFrame->TextRange->Font->Color->RGB => 'RGB(255,255,255)';
## Black
#- $TextBox->TextFrame->TextRange->Font->{Color}->{RGB}=>[255,255,255];
## Black
$Presentation ->SaveAs('<output-filename.ppt>');
You have to pass the value of the VBA RGB function. (Source) That's an integral value, not an array of integer values, or a string.
The value of RGB function can be calculated easily with some simple bit manipulation. The calculation for Red (r), Green (g) and Blue (b) components is:
(r) | (g << 8) | (b << 16)
Where | is the bitwise OR operator and << is the left shift operator. If you'd prefer to work without using bitwise operations, you could also use this calculation:
r + (g * 256) + (b * 65536)