Convert PDF files to PDF/A via Ghostscript - ghostscript

I'd like to convert arbitrary PDF files to PDF/A with Ghostscript 9.15.
Is Ghostscript able to create PDF/A-3b conformant PDFs? There is no parameter which represents a PDF/A conformance level, so I assume there is no possibility. Or is there anything I have overlooked?
I was following a blog post where a Windows batch file is used to convert from PDF to PDF/A (see http://www.mcbsys.com/techblog/2013/04/batch-convert-pdf-to-pdfa/). The gs invokation in the batch is:
"%gs_path%\gswin64c" ^
-dPDFA ^
-dNOOUTERSAVE ^
-sProcessColorModel=DeviceRGB ^
-sDEVICE=pdfwrite ^
-o "GS_%file1%" ^
-dPDFACompatibilityPolicy=1 ^
"%currentdir%\PDFA_def.ps" ^
%inputfilelist%
The PDFA_def.ps is an adjusted version of the official one:
%!
% This prefix file for creating a PDF/A document is derived from
% the sample included with Ghostscript 9.07, released under the
% GNU Affero General Public License.
% Modified 4/15/2013 by MCB Systems.
% Feel free to modify entries marked with "Customize".
% This assumes an ICC profile to reside in the file (AdobeRGB1998.icc),
% unless the user modifies the corresponding line below.
% The color space described by the ICC profile must correspond to the
% ProcessColorModel specified when using this prefix file (GRAY with
% DeviceGray, RGB with DeviceRGB, and CMYK with DeviceCMYK).
% Define entries in the document Info dictionary :
/ICCProfile (... PATH TO ... AdobeRGB1998.icc) % Customize.
def
[ /Title (Title) % Customize.
/DOCINFO pdfmark
% Define an ICC profile :
[/_objdef {icc_PDFA} /type /stream /OBJ pdfmark
[{icc_PDFA} <</N systemdict /ProcessColorModel get /DeviceGray eq {1} {systemdict /ProcessColorModel get /DeviceRGB eq {3} {4} ifelse} ifelse >> /PUT pdfmark
[{icc_PDFA} ICCProfile (r) file /PUT pdfmark
% Define the output intent dictionary :
[/_objdef {OutputIntent_PDFA} /type /dict /OBJ pdfmark
[{OutputIntent_PDFA} <<
/Type /OutputIntent % Must be so (the standard requires).
/S /GTS_PDFA1 % Must be so (the standard requires).
/DestOutputProfile {icc_PDFA} % Must be so (see above).
/OutputConditionIdentifier (AdobeRGB1998) % Customize
>> /PUT pdfmark
[{Catalog} <</OutputIntents [ {OutputIntent_PDFA} ]>> /PUT pdfmark
So, I use AdobeRGB1998.icc which is obviously useable for PDF files with RGB color space. Depending on the -sProcessColorModel value (DEVICERGB) a correct value is printed out.
The conversion works for all files. But when I validate the created PDF file against PDF/A-1b, I get different results depending whether the input file has RGB color space or not (e.g. CMYK). So, when I have an input PDF file which uses CMYK color space, the file gets converted by the script, but the validator says something like this:
input.pdf", 1, 38, 0x03418614, "A device-specific color space (DeviceCMYK) without an appropriate output intent is used.", 1
"output.pdf", 20, 0, 0x83410612, "The document does not conform to the requested standard.", 1
My question: Is there a way to get the conversion done for arbitrary files (i.e. independent of the used color space in the input file)?
Update
#KenS Thanks for your answer. I've updated my initial post to clarify what I want to achieve.
To make it more explicit, I will use an example. There are two files: input1.pdf (seems to use RGB) and input2.pdf (seems to use CMYK). I want to convert both of them to PDF/A-1. Thanks to your hint, I've let go of the above mentioned batch script and instead tested the command directly in the command line. After reading Ps2pdf.htm#PDFA, I have adjusted the (official) PDFA_def.ps so that AdobeRGB1998.icc is used. Then I invoked the following command on both input files (replaced output1.pdf by output2.pdf and input1.pdf by input2.pdf for the second file):
gswin64c.exe -dPDFA=1 -dBATCH -dNOPAUSE -dNOOUTERSAVE \
-sColorConversionStrategy=/RGB \
-sOutputICCProfile=AdobeRGB1998.icc -sDEVICE=pdfwrite \
-sOutputFile=output1.pdf -dPDFACompatibilityPolicy=1 \
"PATH/TO/OFFICIAL/PDFA_def.ps" input1.pdf
The conversion was done without any errors. The output1.pdf seems to be valid, but the output2.pdf is still invalid (tested with 3heights Validator):
"output2.pdf", 1, 40, 0x03418614, "A device-specific color space (DeviceCMYK) without an appropriate output intent is used.", 1
"output2.pdf", 20, 0, 0x83410612, "The document does not conform to the requested standard.", 1
So when I understand your answer correctly, the above command should produce a pdf file which uses the RGB color space - independent of the color space of the input file. If the input file uses CMYK, than the colors have to be translated into RGB with the above command.
When I interpret the first error message correctly, the used color space in the output2.pdf is still CMYK (although the command parameters like ColorConversionStrategy=/RGB). Since I used AdobeRGB1998.icc, the validation error appears.
What am I missing in the above command?
Going back to my original question (which is one step further): Instead of always converting to RGB (or CMYK), I wanted to somehow detect which color space is used in the input file and then dynamically switch to a RGB or CMYK icc file. Is it possible to achieve that?

Ghostscript does not support PDF/A-3. The conformance parameter you are looking for is -dPDFA= where valid values are nothing (defaults to 1), 1 or 2. You can find this documented in ghostpdl/gs/doc/ps2pdf/htm#PDFA
I'm not sure what you are asking for here though. You must either create a PDF/A file (in level 1 or 2 anyway, I haven't read the revision 3 spec yet) which is RGB or CMYK, because you aren't allowed to use both (you can convert everything to device independent colour of course). The colour space used in the input isn't relevant, other than to decide whether it needs to be converted.
This is something you need to decide, we can't decide it for you. One important reason is that the OutputIntent must be consistent with either RGB or CMYK, and the pdfwrite device doesn't check it, it assumes you chose one which matches the device space you are using for the PDF file (by the way, don't set the ProcessColorModel, use ColorConversionStrategy instead) In your case you have set OutputIntent to AdobeRGB1988 so your colours must be specified either in device independent colour, or RGB.
Given the errors you quote, I would suggest the problem is that you haven't specified -sColorConversionStrategy, so the input colours are not being converted to the required device space. I would further guess that the script you copied this from set -dUseCIEColor, and you didn't copy that bit. DO NOT set -dUseCIEColor, its a horrbile ancient piece of PostScript hackery. Instead set ColorConversionStrategy, which will convert colours in a much better way, as required.
Updated answer as this started getting too long for a comment:
I can't immediately see any problems with your command line, can you share an example PDF file ? Its much easier to investigate these things with a solid example. I know from our customers and other free users that pdfwrite is capable of producing conforming PDF/A-1b files.
Regarding the second question; its not possible to do that because currently you need to set the OutputIntentProfile to either a CMYK one or an RGB one before you start. You can't just run through the input PDF file until you come to a colour operation and then decide. If you feel like some programming it could be done by modifying pdfwrite, because the profile isn't actually used till the output is closed.
One problem is that, in order to do the colour conversion, you need to set the underlying ProcessColorModel (this is done for you automatically by ColorConversionStategy). The only way to change ProcessColorModel is to execute a setpagedevice, which causes an erasepage. Now I think that's actually fixable with pdfwrite, all it does is write a white rectangle over the page, so you should be able to intercept that and not emit it. Otherwise any marks you made before you encountered an RGB or CMYK operation would be underneath the white rectangle.....
So essentially no, you can't do it right now, if its important to you then you could probably modify the code to do so (don't forget you will also need to supply 2 OutputIntent profiles to choose between as well). We've never had a customer request to do this, so we won't likely take it on as a project. Of course if you did get this working we might very well incorporate it into the code base if you were to offer it back to us.

Related

What is wrong with this PDF file?

I have to work with a PDF form created by a person unknown to me. Why did the program with which the form was created (Word + PDF export?) split the term "Stunde" into "S", "t" and "unde" in line 6909 of the decoded PDF? There is no visual break between the three parts.
/TT1 1 Tf
11.04 0 0 11.04 59.16 476.1203 Tm
(Datum)Tj
/C2_1 1 Tf
<0003>Tj
/TT1 1 Tf
(der)Tj
0.424 -1.315 Td
(Tätigkeit)Tj
-0.0022 Tc 0 11.04 -11.04 0 261.24 437.7203 Tm
[(Ve)-4.6<7267fc74>-4.2(ungssat)-4.2(z)]TJ
/C2_1 1 Tf
0 Tc <0003>Tj
/TT1 1 Tf
-0.0021 Tc 0.935 -1.315 Td
[<2880>-6.1(/)-7.2(S)0.8(t)-4.1(unde)-4.5(\))]TJ % <<< the important line
0 Tc 11.04 0 0 11.04 340.92 468.8003 Tm
(Anlass/Art)Tj
/C2_1 1 Tf
resulting in
[]
To get the source code above, I decoded the PDF file as described here. I have no know-how concerning the PDF file format.
Background: I had to replace the word "Stunde", it drove me crazy to find the place where "Stunde" was written (in parts) within the source code, since no free PDF editor seems to be able to work with horizontal text without problems.
Academic Bonus questions: Is it possible to set the sum over a column as default value for a form field? (Modifiable; changed every time the column is changed.) Why was I able to replace "Stunde" with "Einsatz" without making the PDF file corrupt due to now irregular offsets?
Why did the program with which the form was created (Word + PDF export?) split the term "Stunde" into "S", "t" and "unde" in line 6909 of the decoded PDF?
As #gettalong mentioned in his answer, in your case this most likely has been done to apply kerning.
If you start looking into the outputs of some other PDF producers, you'll see that this export from Word actually is very unobtrusive in regard to splitting words:
there are PDF producers that draw each character individually after explicitly setting the text matrix for it, and
there also are PDF producers that have the width information for the characters of the used fonts set to zero and use the numbers in TJ instructions to forward the current text matrix between characters accordingly.
And this doesn't cover all the variants to be found, not by far...
Thus,
I had to replace the word "Stunde", it drove me crazy to find the place where "Stunde" was written (in parts) within the source code
in your case replacing actually was a fairly trivial task...
Is it possible to set the sum over a column as default value for a form field? (Modifiable; changed every time the column is changed.)
If all the column values in question are stored in form fields, you can use JavaScript to recalculate sums after form changes. To have it serve as "default" only, you can use some other (hidden) field for a flag whether the field has already been touched. Beware, though: JavaScript is not supported by all PDF viewers. Furthermore, the JavaScript object model for PDF is not specified in an independent (like ISO) specification but in an Adobe one which can make interpretation of the specification biased.
Why was I able to replace "Stunde" with "Einsatz" without making the PDF file corrupt due to now irregular offsets?
As we don't know how exactly you applied the changes, this obviously is hard to tell.
Most likely, though, you did corrupt the PDF and the PDF viewers you opened it in merely repair the corruption under the hood. There is a strong tendency in PDF viewers to do such under-the-hood repairs without informing the user; the result is that a large part of the PDFs in the wild actually being broken.
You don't see a visual break but the standard distance between "S", "t" and "unde" has been changed nonetheless. This is done by PDF writers that support e.g. kerning so that the word appear nicer. This is the reason why it is split that way.

merge pdf files with extra blank page at the end of odd-paged documents - qpdf

i'm hoping to use qpdf for this.
I'm printing lots of small files and need to print them double sided, so I merge, say, 20 documents and wind up with a single 200 page pdf. I then can let the printer print, even pages reversed, then flip the stack over and put it back into the printer and print the odd ones, so we're using both sides of the paper.
my question is how i can detect and add a single blank page to the end of any document that has an odd number of pages; that way, when i do double sided printing, each document is completely separate from the others, rather than just printing on the back of a finished document.
If you have an odd number of pages, just call
cpdf -pad-multiple
with the even number one larger than the odd number. For example, for 19 pages, run
cpdf -pad-multiple 20 in.pdf -o out.pdf
You can get the number of pages with cpdf -pages.
Currently using FOSS qpdf 10.6.3 this is possible in windows by something like (note %% is for use in a batch.cmd)
for /f %%N in ('qpdf --show-npages in.pdf') do set VAR=%%N& set /a num=2*(%%N/2)+1& if .%num%.==.%var%. qpdf in.pdf --pages . blankA4P.pdf -- out.pdf
Note the integer maths is rounding odds down so 31 = 30+1 is a match but 32 = 32+1 will not match.
Without checking the dimensions of in.pdf is not easy to know if blankA4P.pdf is required so best to either get last page dimensions for a matching prepared page or batch apply each shape in groups.
using cpdf (as mentioned in previous answer) we could build a blank on demand along the lines of cpdf -create-pdf -create-pdf-pages 1 -o blank.pdf and use a page size, However cpdf has the even better option for OP case so simplest is cpdf -pad-multiple 2 in.pdf -o out.pdf as 1st hinted by johnwhittington
So now we have a perfect short one line solution. however cpdf is not FOSS
I have queried with qpdf if there may be a simpler way with just qpdf so watch this space https://github.com/qpdf/qpdf/issues/753
I will continue looking for a way to streamline this further because I would love a simpler workflow, but here is what I use to ensure I do not have two articles sharing one sheet of paper.
pacman::p_load(tidyverse, pdftools, qpdf)
# some prep
directory <- "Some/FileFolder/Path"
filelist <- (paste(directory, "/",
list.files(directory,
pattern = "*.pdf"), sep = ""))
# bust apart all pdf to inspect
my_pages <- as.list(lapply(filelist, pdf_split))
page_summ <- cbind.data.frame(filelist,
lengths(my_pages)) %>%
rename(filename = 1,
pages = 2) %>%
mutate(is_odd = pages %%2==1)
# separate into odd and even sets
odd_docs <- page_summ %>%
filter(is_odd == TRUE)
even_docs <- page_summ %>%
filter(is_odd == FALSE)
# I could not find an R process for adding a page to PDFs.
# For now, I will add a buffer page to docs via a PDF program.
# Once you are satisfied with your even_docs subset, pdf_combine
tocombine <- as.data.frame(even_docs$filename)
lapply(tocombine, pdf_combine)
This auto-generates the combo file into the previously defined directory. The new file name is not able to be set using "output = " within lapply(). Look for new file name = "firstnameintocombine_combined.pdf".

Remove all metadata from an image on the command line

So I used exiftool -all= command line tool to remove the metadata from an image. However, when I print the metadata of the resulting image, I get this:
$ exiftool myimage.jpg
ExifTool Version Number : 11.30
File Name : myimage.jpg
Directory : out
File Size : 2.8 MB
File Modification Date/Time : 2019:05:16 03:34:02-07:00
File Access Date/Time : 2019:05:16 03:34:02-07:00
File Inode Change Date/Time : 2019:05:16 03:34:02-07:00
File Permissions : rw-r--r--
File Type : JPEG
File Type Extension : jpg
MIME Type : image/jpeg
DCT Encode Version : 100
APP14 Flags 0 : [14]
APP14 Flags 1 : (none)
Color Transform : YCbCr
Image Width : 3729
Image Height : 2246
Encoding Process : Baseline DCT, Huffman coding
Bits Per Sample : 8
Color Components : 3
Y Cb Cr Sub Sampling : YCbCr4:4:4 (1 1)
Image Size : 3729x2246
Megapixels : 8.4
I am wondering a few things:
If it is required at some level to have all of this (albeit minimal) metadata. That is, I'm wondering if we could get even more minimal and really remove all metadata.
If we can't remove all the metadata that remains, I'm wondering if I can at least remove the first 3 attributes (ExifTool Version Number, File Name, and Directory).
If any of this is possible, wondering what tool (preferrably command-line tool) could accomplish this.
Almost all that remaining data is not metadata embedded in the file. They are properties of the image or the underlying OS. Or even in the case of ExifTool Version Number, the version of exiftool you are running. The '-g' and '-G' options can show you which group each tag comes from; in particular, group families 0 and 1 can be used to see the tags that come from ExifTool and file attributes, as well as those embedded in the image file itself.
The items such as permissions, file name, directory, and the time stamps are taken directly from the underlying OS. Those are properties of every single file on the drive. Without them, the file itself does not exist.
The file/Mime type entries are properties of the file that exiftool has created when it figured out what kind of file it is.
Except for the APP14 entries, the rest are data about the image itself. How it is encoded, the format of the encoding blocks, the size of the image, etc.
The only thing that is embedded in this image is the APP14 block. This block contains no data that can identify the origin of the image. But there is a chance that removing it will significantly alter the colors of the image (see this post). It can be removed by adding -Adobe:All= to the command.

Other options to resize barcode for zebra printer using ZPL?

I want to print a Code 128 barcode with a Zebra printer. But I just can't get exactly where I want because the barcode is either too small or too big for the label size of 40x20 mm. Is there anything else I can try besides using the ^BY (Bar Code Field Default) module width and ratio?
^XA^PQ2^LH0,0^FS
^MUM
^GB40,20,0.1,B^FS
^FO1.5,4
^BY0.2
^BCN,10,N,N
^FD*030493LEJCG002999*^FS
^FO8,15
^A0N,3,3
^FD*030493LEJCG002830*^FS
^MUD
^XZ
Above script gives me a label that looks like this:
But when I just decrease the module width to 0.1 (which is the lowest) the barcode becomes too small and may be problematic to scan with a hand scanner:
Code-128 is a fixed-ratio code, so you would appear to have the choice of two sizes. You may be able to solve the problem by using a 300dpi printer in place of a 200.
If you can change the format (and I'm intrigued by the barcode and readable being different values) then you could save a little by printing one number-sequence and one alpha-sequence, as an even count of numerics will be encoded as alphabet C so you'd save one change-alphabet element.
Do you really need the * on each end?
Otherwise, perhaps code 39 (which prints the * if you use the print-interpretation-line option) would suit your purposes better.
Another Possibility is to do on the fly code-set changes, Try something like
^XA^PQ2^LH0,0^FS
^MUM
^GB60,20,0.1,B^FS
^FO1.5,4
^BY0.2
^BCN,10,N,N
^FD>:*>5030493>6LEJCG>5002830>6*^FS
^FO8,15
^A0N,3,3
^FD*030493LEJCG002830*^FS
^MUD
^XZ
This will allow less symbols to encode your data
If you can structure content to have all the alpha chars a one end or the other.
or (Depending on your firmware) you could use auto ^BCN,10,N,N,N,A

Editing thickness in postscript (.ps or .eps) figures via unix shell commands?

I have many figures (graphs) in postscript (.eps) format that I wish to thicken the plots with.
I found the following code, but the output file is no different. I was wondering what I was doing wrong.
The code:
# get list of all arguments
set args = ($*)
# if not enough arguments, complain.
if ($#args < 2) then
echo "Usage: ps_thicken ps_file factor"
echo "Thickens all lines in a PostScript file by changing the linewidth macro."
echo "Result goes to standard output."
exit 1
endif
sed -e "s/^\/lw {\(.*\) div setlinewidth/\/lw {$2 mul \1 div setlinewidth/" $1
Now to execute this from my command line, I use the command (filename is ps_thicken, and has appropriate permissions):
./ps_thicken old_file.eps 10 > new_thick_file.eps
Which I thought should make everything 10x thicker, but it just doesnt change anything.
Any help would be greatly appreciated, I'm pretty new to shell script!
PostScript is a programming language, so it isn't really possible to make changes in an automated fashion like this. At least not without writing a PostScript program to do so!
Note that linewidth isn't a 'macro' (PostScript doesn't have macros) its am operator. What the code you've posted for sed does (if I recall sed well enough) is look for the definition of /lw and replace it with a modified version. The problem with that is that /lw is a function declartation in a particular PostScript program. Most PostScript programs won't have (or use) a function called 'lw'.
You would be much better to prepend the PostScript program code with something like:
/oldsetlinewidth /linewidth load def
/setlinewidth {2 div oldsetlinewidth} bind def
That will define (in the current dictionary) a function called 'setlinewidth'. Now, if the following program simply uses the current definition of setlinewdith when creating its own functions, it will use the redefined one above. Which will have the effect of dividing all line widths by 2 in this case. Obviously to increase the width you would use something like 2 mul instead of 2 div.
Note that this is by no means foolproof, its entirely possible for a PostScript program to explicitly load the definition of setlinewidth from systemdict, and you can't replace that (at least not easily) because systemdict is read-only.
However its unlikely that an EPS program would pull such tricks, so that should probably work well enough for you.
[based on comments]
Hmm, you mean 'failed to import' into an application or something else ?
If you're loading the EPS into an application then simply putting that code in front of it will break it. EPS (unlike PostScript) is required to follow some rules, so to modify it successfully you will have to follow them. This includes skipping over any EPS preview.
This is not really a trivial exercise. Your best bet is probably to run the files through Ghostscript, you can do a lot by harnessing a PostScript interpreter to do the work.
Start with the 2 lines of PostScript above in a file, then run the EPS file you want to 'modify' through Ghostscript, using the eps2write device. That will produce a new EPS which has the changes 'baked in'.
Eg (assuming the linewidth modifying code is in 'lw.ps'):
gs -sDEVICE=eps2write -o out.eps lw.ps file.eps
But be aware that the resulting EPS is a completely rewritten program and will bear no relation to the original. In particular any preview thumbnail will be lost.

Resources