How do I get output file name containing page X of Y - ghostscript

I am using ghostscript to convert multipage pdf to png. I need to have the output filename of the png file as pageXofY.png where X is the page number and Y is the total number of pages in the pdf.
I know I can use page%d.png to get the current page number.
Can anyone help please? I have number of multipage pdf so manually renaming the png files is not an option.
Thank you.

You can't do this in standard Ghostscript. The primary reason is that the device has no way to know the number of pages in the input. While this can be determined for a single input PDF file, if you had 2 input files, how would you know (while processing file 1) what the total number of pages in all the files was ?
You might be able to use the PDF interpreter to reset the OutputFile for the device, once the number of pages has been determined.
In /ghostpdl/gs/Resource/Init/pdf_main.ps look for the procedure /runpdfpagerange, the routine terminates:
QUIET not {
(Processing pages ) print 1 index =only ( through ) print dup =only
(.) = flush
} if
} ifelse
} ifelse
} bind def
Immediately in front of the "}bind def" you could try adding something like:
10 LastPage cvs %% (LastPage)
dup length 15 add string %% (LastPage) string
dup (Page %d of ) exch copy %% (LastPage) (Page %d of ) (Page %d of)
dup 12 3 index %% (LastPage) (Page %d of ) (Page %d of) 12 (LastPage)
putinterval %% (LastPage) (Page %d of LastPage)
dup 3 1 roll %% (Page %d of LastPage) (Page %d of LastPage) (LastPage)
length 12 add %% (Page %d of LastPage) (Page %d of LastPage) n
(.png) putinterval %% (Page %d of LastPage.png)
<< /OutputFile 3 1 roll>> setpagedevice
Which might work, I haven't tried it.
Alternatively, use Ghostscript's pdf_info.ps to determine the number of pages in the input PDF file, then invoke Ghostscript a second time creating the OutputFile from the information returned by pdf_info.ps (eg -sOutputFile="Page %d of 100.png")
update
pdf_info.ps is distributed as part of the Ghostscript source, its not in the prepackaged Windows installer so you would need to pick up a copy of the source distribution (www.ghostscript.com, just download the sources rather than the Windows installer).
The file can be found in ghostpdl/gs/toolbin, if you open it with a text editor you'll see the usage near the top in a comment. Here's an example of the output:
D:\tests>\ghostpdl\gs\debugbin\gswin32c -dNODISPLAY -sFile=01_001.pdf /ghostpdl/
gs/toolbin/pdf_info.ps
GPL Ghostscript GIT PRERELEASE 9.16 (2014-09-22)
Copyright (C) 2014 Artifex Software, Inc. All rights reserved.
This software comes with NO WARRANTY: see the file PUBLIC for details.
01_001.pdf has 9 pages
Title: 571l_p
Author: mwang
Subject: 571l_p
Keywords:
Creator: Adobe PageMaker 6.52
Producer: Acrobat Distiller 4.05 for Windows
CreationDate: D:20001113140954
ModDate: D:20010305115330-08'00'
Page 1 MediaBox: [0 0 612 792] CropBox: [0 0 612 792] Rotate = 0
Page 2 MediaBox: [0 0 612 792] CropBox: [0 0 612 792] Rotate = 0
Page 3 MediaBox: [0 0 612 792] CropBox: [0 0 612 792] Rotate = 0
Page 4 MediaBox: [0 0 612 792] CropBox: [0 0 612 792] Rotate = 0
Page 5 MediaBox: [0 0 612 792] CropBox: [0 0 612 792] Rotate = 0
Page 6 MediaBox: [0 0 612 792] CropBox: [0 0 612 792] Rotate = 0
Page 7 MediaBox: [0 0 612 792] CropBox: [0 0 612 792] Rotate = 0
Page 8 MediaBox: [0 0 612 792] CropBox: [0 0 612 792] Rotate = 0
Page 9 MediaBox: [0 0 612 792] CropBox: [0 0 612 792] Rotate = 0
Fonts Needed that are not embedded (system fonts required):
Arial
Arial,Bold
D:\tests>
So you would need to output that to a file and scan it, or pipe the output through grep or something similar to find the number of pages.

Related

Edit (every page of a) Postscript file manually

I want to (manually) insert additional postscript commands in a postscript file that was converted from a PDF file via ghostscript’s pdf2ps. For the purpose of testing I created a PDF file from the following file using pdflatex:
\documentclass[a4paper]{article}
\begin{document}
Mostly empty.
\end{document}
In the converted postscript file I make the following edit:
...
%%Page: 1 1
%%PageBoundingBox: 0 0 595 841
%%BeginPageSetup
4 0 obj
<</Type/Page/MediaBox [0 0 595.28 841.89]
/Parent 3 0 R
/Resources<</ProcSet[/PDF]
/Font 8 0 R
>>
/Contents 5 0 R
>>
endobj
%%EndPageSetup
% BEGIN MANUAL EDIT
0 setgray 0 0 moveto 595 841 lineto stroke
% END MANUAL EDIT
5 0 obj
<</Length 257>>stream
q 0.1 0 0 0.1 0 0 cm
0 G
0 g
q
10 0 0 10 0 0 cm BT
/R6 9.9626 Tf
1 0 0 1 139.746 706.129 Tm
[(M)-0.699638(os)-0.399443(t)-0.900585(l)-0.798886(y)-333.819(e)-0.400668(m)-0.300195(p)-0.599165(t)26.0974(y)83.192(.)-0.800112]TJ
154.421 -615.691 Td
(1)Tj
ET
Q
Q
endstream
endobj
pagesave restore
%%PageTrailer
%%Trailer
end
%%EOF
Instead of producing a diagonal line the postscipt/PDF file remains (seemingly) unchanged. However, if I alter the page dimensions from A4 to letter size the line is shown:
%%Page: 1 1
%%PageBoundingBox: 0 0 612 792
%%BeginPageSetup
4 0 obj
<</Type/Page/MediaBox [0 0 612 792]
...
I am obviously missing something here (which is not surprising given my rudimentary knowledge of postscript). My question is: How can I make the line appear while keeping the page dimensions unchanged?
P.S.: A comment I stumbled across mentioned that pdftops (from poppler-utils) is in some sense superior to pdf2ps. Indeed, inserting commands into the converted postscript file immediately before the showpage command (which is not there at all when using pdf2ps) worked fine. So I have probably already found a solution to my problem. However, I would like to learn what the page dimensions have to do with it when using pdf2ps.
Solution
Thanks to KenS’s advice and referring to his answer to this question I was able to achieve the desired effect by adding an EndPage procedure to the postscript file:
<<
/EndPage
{
exch pop 2 lt
{
gsave
0 0 translate
0 setgray 0 0 moveto 596 842 lineto stroke
grestore
true
}{false} ifelse
} bind
>> setpagedevice
(This assumes the page size is a4.)
PostScript is a write-only language :-)
Seriously, its a programming language. In order to understand what's going on, you need to understand the program, which in the case of the output from Ghostscript's ps2write device, is distinctly non-trivial.
The syntax is basically PDF, with a prolog program which interprets it in PostScript terms.
The program will use showpage, it does it when the procedure EndStream is executed, which is (basically) when the endobj keyword is encoutnered in a page stream. You'll see that it looks like:
ET
Q
Q
Q
endstream
endobj
%%Page: 2 2
You could place anything you like between the endstream and the endobj, but you need to be aware that the graphics state at that point is determined by whatever operations have already taken place. This can include scaling, oration, skeing, flipping the vertical axis, etc. So simply inserting some PostScript into there is unlikely to work. You could do an initgraphics which would at least reset the graphics state to a known setup.
As a test I ran Ghostscript'sd ps2write device like this:
gs -sDEVICE=pdfwrite -o out.ps -c "showpage" -f
which produces a PostScript program where the (effective) content is:
%%EndResource
%%EndProlog
%%Page: 1 1
%%PageBoundingBox: 0 0 595 842
%%BeginPageSetup
4 0 obj
<</Type/Page/MediaBox [0 0 595 842]
/Parent 3 0 R
/Resources<</ProcSet[/PDF]
>>
/Contents 5 0 R
>>
endobj
%%EndPageSetup
5 0 obj
<</Length 23>>stream
q 0.1 0 0 0.1 0 0 cm
Q
endstream
endobj
%%Trailer
end
%%EOF
I then modified this more or less as you suggested:
%%EndPageSetup
0 setgray 0 0 moveto 595 842 lineto stroke
5 0 obj
<</Length 23>>stream
q 0.1 0 0 0.1 0 0 cm
Q
endstream
endobj
%%Trailer
For me this produced the expected stroke from bottom left to top right. Obviously without the PostScript file you originally produced I can't tell you why your experience is different. (no I'm not in a position to run latex to produce such a thing, and even if I did I have no way to know which version of Ghostscript and the other tools you used).
My guess would be that 'something' in your PDF file overwrote the entire page, its not entirely uncommon.

PS easy way to color text

I have a postscript file that contains a phylogenetic tree outputted by njplot. It basically consists of lines and labels at the end of the line. Right now it is in black and white but I would like to mark differences between different trees:
Below is a short extract from one of my files with only three of the labels.
a) What do I have to do to make e.g. "B. ovis 25840" be displayed in red?
b) How can I make a box around "B. suis 23445" and "B. Thomsen" (such as to mark that they are in the same group?)
/setpacking where {true setpacking} if
1 setlinecap 1 setlinejoin 1 setlinewidth 0 setgray
/basefont /Times-Roman findfont 12 scalefont def
/titlefont /Times-Roman findfont 12 scalefont def
/setclip {40 40 moveto 560 40 lineto 560 810 lineto 40 810 lineto closepath clip newpath} def
/title {titlefont setfont
40 815 moveto (brucella_conc_se_ani.out_nj.outtree Mon Aug 14 14:52:28 2017
) show ( Page ) show show ( of 1) show
} def
%%EndProlog
%%Page: ? 1
(1) title setclip
0 0 translate
basefont setfont
50 50 translate
0.7 setgray -10 -10 moveto 510 -10 lineto 510 760 lineto -10 760 lineto closepath stroke 0 setgray
359 8 moveto
(B. ovis 25840) show
298 67 moveto
(B. Thomsen) show
294 127 moveto
(B. suis 23445) show
showpage
Text is drawn (for text in fonts other than type 3) using the current color. So if you alter the current color before drawing the text, then it will be drawn in a different color.
There are a number of color operators in PostScript, the simplest are the setgray, setrgbcolor and setcmykcolor operators. setgray takes a value between 1 (white) and 0 (black) and sets the current color to that percentage of gray. setrgbcolor takes 3 parameters between 01 and 1 for each of R, G and B, and setcmykcolor does the same but with CMYK components.
So to answer question 'a' 1 0 0 setrgbcolor placed before the line with the text on it, would cause the text to be drawn in red.
Note that all subsequent operations would also be rendered in red, so we should consider now the gsave and grestore operators. gsave saves a copy of the graphics state, and grestore (surprise!) restores the graphics state from the most recently saved version.
You can use this to limit the effect of a graphics state change. For example:
gsave
1 0 0 setrgbcolor
(B. ovis 25840) show
grestore
would render the text in red, but the subsequent text would be in the colour in force before the gsave, presumably black.
Note that the current point on the page is part of the gstate! So :
gsave
1 0 0 setrgbcolor
359 8 moveto
(B. ovis 25840) show
298 67 moveto
grestore
(B. Thomsen) show
would cause the second 'show' to take place at the same position as the first one, overwriting it.
You don't 'draw a box', PostScript doesn't have graphics primitives like that. What you do is construct a path, and then stroke it (you could fill it instead for a filled rectangle).
For example:
0 0 moveto
0 100 lineto
100 100 lineto
100 0 liento
closepath
stroke
will construct a rectangular path with its bottom left corner at 0,0 it will be 100 units wide and tall (so a square). The 'stroke' operator then strokes the path using the current linewidth and the current colour.
If you want to dig deeper into PostScript then you will need a copy of the PostScript Language Reference Manual, which is available online in PDF format from the Adobe web site.
You might also like to look at the the 'Blue Book' which is also available there as the 'PostScript language tutorial and cookbook'

PostScript error with imagemask and raw data

In Adobe's PLRM
I found the following example using the imagemask operator.
This works fine when running with Ghostscript.
54 112 translate % Locate lower-left corner of square
120 120 scale % Scale 1 unit to 120 points
0 setgray % Set current color to black
24 23 % Specify dimensions of source mask
true % Set polarity to paint the 1 bits
[24 0 0 -23 0 23] % Map unit square to mask
{< 003B00 002700 002480 0E4940
114920 14B220 3CB650 75FE88
17FF8C 175F14 1C07E2 3803C4
703182 F8EDFC B2BBC2 BB6F84
31BFC2 18EA3C 0E3E00 07FC00
03F800 1E1800 1FF800 >}
imagemask
showpage
As an exercise I tried to rewrite the above example using an ImageType-1 dictionary and raw data, and finally came up with this code:
54 112 translate
120 120 scale
0 setgray
<<
/ImageType 1
/Width 24
/Heigth 23
/BitsPerComponent 1
/Decode [1 0]
/ImageMatrix [24 0 0 -23 0 23]
/DataSource currentfile /ASCIIHexDecode filter
>>
imagemask
003B00 002700 002480 0E4940
114920 14B220 3CB650 75FE88
17FF8C 175F14 1C07E2 3803C4
703182 F8EDFC B2BBC2 BB6F84
31BFC2 18EA3C 0E3E00 07FC00
03F800 1E1800 1FF800>
showpage
However, when running this with Ghostscript I get the following error.
Error: /undefined in --imagemask--
I'm still scratching my head to find the bug, but in vain.
How can it be imagemask is undefined? Or did I miss something obvious?
I don't know if this is exactly the code you've written, but there's a typo:
/Heigth 23
which should obviously be:
/Height 23
If I correct that, the file runs to completion, and draws the turkey.

Replacing selective numbers with NaNs

I have eight columns of data. Colulmns 1,3, 5 and 7 contain 3-digit numbers. Columns 2,4,6 and 8 contain 1s and zeros and correspond to 1, 3, 5 and 7 respectively. Where there is a zero in an even column I want to change the corresponding number to NaN. More simply, if it were
155 1 345 0
328 1 288 1
884 0 145 0
326 1 332 1
159 0 186 1
then 884 would be replaced with NaN, as would 159, 345 and 145 with the other numbers remaining the same. I need to use NaN to maintain the data in matrix form.
I know I could use
data(3,1)=Nan; data(5,1)=Nan
etc but this is very time consuming. Any suggestions would be very welcome.
Approach 1
a1 = [
155 1 345 0
328 1 288 1
884 0 145 0
326 1 332 1
159 0 186 1]
t1 = a1(:,[2:2:end])
data1 = a1(:,[1:2:end])
t1(t1==0)=NaN
t1(t1==1)=data1(t1==1)
a1(:,[1:2:end]) = t1
Output -
a1 =
155 1 NaN 0
328 1 288 1
NaN 0 NaN 0
326 1 332 1
NaN 0 186 1
Approach 2
[x1,y1] = find(~a1(:,[2:2:end]))
a1(sub2ind(size(a1),x1,2*y1-1)) = NaN
I would split the problem into two matrices, with one being a logical mask, the other holding your data.
data = your_mat(:,1:2:end);
valid = your_mat(:,2:2:end);
Then you can simply do:
data(~valid)=NaN;
You could then rebuild your data by doing:
your_mat(:,1:2:end) = data;
Here is an interesting solution, I would expect it to perform quite well, but be aware that it is a bit tricky!
data(~data(:,2:end))=NaN
Using logical indexing:
even = a1(:,2:2:end); % even columns
odd = a1(:,1:2:end); % odd columns
odd(even == 0) = NaN; % set odd columns to NaN if corresponding col is 0
a1(:,1:2:end) = odd; % assign back to a1
a1 =
155 1 NaN 0
328 1 288 1
NaN 0 NaN 0
326 1 332 1
NaN 0 186 1
Here is an alternative solution. You can use circshift, in the following manner.
First create a mask of the even columns of the same size of your input matrix A:
AM = false(size(A)); AM(:,2:2:end) = true;
Then circshift the mask (A==0)&AM one element to the left, to shift this mask on the odd columns.
A(circshift((A==0)&AM,[0 -1])) = nan;
NOTE: I've searched for a one-liner ... I don't think it's a good one, but here is one you can use, based on my solution:
A(circshift(bsxfun(#and, A==0, mod(0:size(A,2)-1,2)),[0 -1])) = nan;
The dirty thing with bsxfun is to create on-line the mask AM. I use for that the oddness test on a vector of indices, bsxfun extends it over the whole matrix A. You can do anything else to create this mask, of course.

How can I draw a triangle in an image in MATLAB?

I need to draw a triangle in an image I have loaded. The triangle should look like this:
1 0 0 0 0 0
1 1 0 0 0 0
1 1 1 0 0 0
1 1 1 1 0 0
1 1 1 1 1 0
1 1 1 1 1 1
But the main problem I have is that I do not know how I can create a matrix like that. I want to multiply this matrix with an image, and the image matrix consists of 3 parameters (W, H, RGB).
You can create a matrix like the one in your question by using the TRIL and ONES functions:
>> A = tril(ones(6))
A =
1 0 0 0 0 0
1 1 0 0 0 0
1 1 1 0 0 0
1 1 1 1 0 0
1 1 1 1 1 0
1 1 1 1 1 1
EDIT: Based on your comment below, it sounds like you have a 3-D RGB image matrix B and that you want to multiply each color plane of B by the matrix A. This will have the net result of setting the upper triangular part of the image (corresponding to all the zeroes in A) to black. Assuming B is a 6-by-6-by-3 matrix (i.e. the rows and columns of B match those of A), here is one solution that uses indexing (and the function REPMAT) instead of multiplication:
>> B = randi([0 255],[6 6 3],'uint8'); % A random uint8 matrix as an example
>> B(repmat(~A,[1 1 3])) = 0; % Set upper triangular part to 0
>> B(:,:,1) % Take a peek at the first plane
ans =
8 0 0 0 0 0
143 251 0 0 0 0
225 40 123 0 0 0
171 219 30 74 0 0
48 165 150 157 149 0
94 96 57 67 27 5
The call to REPMAT replicates a negated version of A 3 times so that it has the same dimensions as B. The result is used as a logical index into B, setting the non-zero indices to 0. By using indexing instead of multiplication, you can avoid having to worry about converting A and B to the same data type (which would be required to do the multiplication in this case since A is of type double and B is of type uint8).

Resources