Graphviz multiple files format conversion - batch-processing

I want to convert several files (>100) from .dot format (generated by Doxygen) to .png format, using Graphviz.
Following page describes the file conversion for only one file:
Graphviz: How to go from .dot to a graph?
dot -Tpng input.dot > output.png
Now, I want to proceed with a massive conversion proccess, which involves a lot of files inside a directory.
Most of documentation is for Linux OS, like the following:
https://github.com/rakhimov/cppdep/wiki/How-to-view-or-work-with-Graphviz-Dot-files
( as example:
To run dot on files in directories and sub-directories recursively:
$ find -type f -name "*.dot" directory_path | xargs dot -Tpdf -O )
but I really need to do it on Windows OS.
I have been trying with the following command:
forfiles /c "dot -Tpng #file > #fname.png"
however, in this case, Windows cmd shows the error message:
"There is no layout engine support for "-tpng"
Use one of: circo dot fdp neato nop nop1 nop2 osage patchwork sfdp twopi"
for each one of the file conversion attemps.
How it can be done?
Thanks for your time.

Try this, it worked for me:
forfiles /m "*.dot" /c "cmd /c dot -Tpng #file > #fname.png"
or, if the extension is ".gv"
forfiles /m "*.gv" /c "cmd /c dot -Tpng #file > #fname.png"

Related

Add quotation mark in CMake string

I am using CMake to create and build my project solution. i am using the following command to add a post build event to copy a .tlb from the local bin to the program bin.
ADD_CUSTOM_COMMAND(TARGET ${PROJECT_NAME} POST_BUILD COMMAND xcopy /D /Y "${CMAKE_SOURCE_DIR}LocalBin\\example.tlb" "${CMAKE_SOURCE_DIR}ProgramBin\\$<CONFIGURATION>\\")
When this adds the command to the project properties , it is added as
xcopy /D /Y LocalBin\example.tlb ProgramBin\Debug\
However this gives me an error. Exited with Code 4.
If i go into the project properties and hack the command line and change it to add " "
xcopy /D /Y "LocalBin\example.tlb " "ProgramBin\Debug\"
It works.
Is there a way i can change the CMake add custom command to include the " " in the actual command line so it will work and there is no need to manually change the project properties.
You have to escape the quotation mark inside the string. Escaping is done in CMake with a back slash. So adding a quotation mark to your string, add \".
Quoting the CMake documentation https://cmake.org/cmake/help/v3.4/manual/cmake-language.7.html#escape-sequences
A \ followed by a non-alphanumeric character simply encodes the literal character without interpreting it as syntax
In your case you'll end up with
"\"${CMAKE_SOURCE_DIR}LocalBin\\example.tlb\""
instead of
"${CMAKE_SOURCE_DIR}LocalBin\\example.tlb"
After struggling with this for some time, I found a workable solution using generator expressions:
ADD_CUSTOM_COMMAND(TARGET ${PROJECT_NAME} POST_BUILD COMMAND xcopy /D /Y $<1:"${CMAKE_SOURCE_DIR}LocalBin\\example.tlb"> $<1:"${CMAKE_SOURCE_DIR}ProgramBin\\$<CONFIGURATION>\\">)
This is an open bug in CMake.
Sadly this works only for the command arguments, but not for the command itself. This is really no fun when you have to call for example shader compiler that is in the Program Files (path with space in it) and prebuild rule fails. To work around this problem I created batch file on the fly with cmake file(WRITE ...) command and then call this batch file with correctly escaped path to the actual shader compiler I want to execute. In the batch I have a line with %1
Solution for my problem
file(WRITE Shaders/compile_shaders.bat "\
%1 /ESolveVMF /Tcs_5_0 /DTGSize_=16 /Fh \"${CMAKE_CURRENT_LIST_DIR}/Shaders/GenerateRoughnessMaps.inc\" \"${CMAKE_CURRENT_LIST_DIR}/Shaders/GenerateRoughnessMaps.hlsl\"\n\
%1 /ESolveVMFReconstructNormal /Tcs_5_0 /DTGSize_=16 /Fh \"${CMAKE_CURRENT_LIST_DIR}/Shaders/GenerateReconstructNormalRoughnessMaps.inc\" \"${CMAKE_CURRENT_LIST_DIR}/Shaders/GenerateRoughnessMaps.hlsl\"")
add_custom_command(TARGET ${CURRENT_MODULE_NAME} PRE_BUILD
COMMAND "${CMAKE_CURRENT_LIST_DIR}\\Shaders\\compile_shaders.bat" "\"$(WindowsSdkDir)bin\\x86\\fxc.exe\""
COMMENT "Compiling shaders")

Find and Replace text in .sql files

I have many .sql files in subfolders. I am presently manually opening them up, and searching for OLDSERVERNAME, and replacing it with NEWSERVERNAME(I'm doing migration). There must be a faster way to do this. I tried using FART, but I guess I wasn't doing it right.
This is what I tried(in main folder):
fart -i -p -c *.sql OLDSERVERNAME NEWSERVERNAME
Can I perhaps use unix utilities for this purpose?
You can use sed for this. sed stands for S tream Ed itor
sed -i 's/OLDSERVERNAME/NEWSERVERNAME/g' *.sql
-i option will do in-file substitution.
g implies global substitution. So if there are more than one instances of OLDSERVERNAME in one line they will get replaced with NEWSERVERNAME
*.sql will pass all files ending with .sql extension.
Look up sed man page for more details.
On MacOS - I had to add a backup file extension
sed -i '.bak''s/OLDSERVERNAME/NEWSERVERNAME/g' *.sql

Merged PDFs Blank

Trying to merge all pdfs in a directory using GhostScript 9.06 64bit in a .bat file
The following, makes merged.pdf, but it is 1 page and blank
call gswin64c -q -dNOPAUSE -dBATCH -sDEVICE=pdfwrite -sOutputFile=merged.pdf *.pdf
If I actually specify which PDFs to merge it works fine. What gives?
call gswin64c -q -dNOPAUSE -dBATCH -sDEVICE=pdfwrite -sOutputFile=merged.pdf 1.pdf 2.pdf 3.pdf
You can't specify wildcards on the Ghostscript command line, simple as that.
Since GS didn't find a file called '*.pdf' it didn't execute any marking operations, in this case you get a blank file.
Ghostscript cannot do wildcard expansions by itself.
If you call gs ... *.pdf from inside a shell which can do wildcard expansion, it will work nevertheless.
There is a difference with the site you linked to and the code you used above:
Your code is DOS batch and uses call gswin64c .... But as said, Ghostscript cannnot expand wildcards itself.
The code in the linked web page is Unix shell, which does the wildcard expansion before Ghostscript gets to see its own commandline. When Ghostscript gets to see it, the wildcard expansion has happened already.
You have to find a solution for your batch file where you first store your (expanded) *.pdf file names in a variable %mypdfs% and then do call gswin64c ... %mypdfs%.
you can't specify the wildcard from the command line, but you can make gswin32c run a command file.
as the 'command file' just requires switches to be separated by any amount of white space (space, tab, line break), and there is no limit on the size of the file, we can make a file that does what you need
echo -q -dNOPAUSE -dBATCH -sDEVICE=pdfwrite -sOutputFile=merged.pdf > files.gsx
dir *.pdf /b >> files.gsx
once this file files.gsx has been created, then you can make your file using
gswin32c #files.gsx
and all the files will be merged
I did the following to solve this:
1.) dir /B *.pdf > do.bat
2.) opened do.bat with notepad to replace \r\n with spaces
3.) inserted: c:\Programs\gs\gs9.07\bin\gswin64 -dNOPAUSE -sDEVICE=pdfwrite -sOutputFile=merged.pdf at the beginning
and then executed do.bat
VoilĂ 

Running Command Line Arguments

I'm trying to run a command line argument through VB.NET using the Shell() command.
I'm trying to use this piece of code:
FOR /R %I in (*.pdf) DO #pdf2swf.exe "%~fI" -o "%~dpI%~nI.swf" -f -T 9
-t -G
Using this:
Shell("FOR /R %I in (*.pdf) DO #pdf2swf.exe "%~fI" -o "%~dpI%~nI.swf" -f -T 9
-t -G ")
However, the interpreter is giving me this error:
Character is not valid. (BC30037)
For the %~ part.
I also tried created a string and passing the argument to the Shell() command by using Shell(StringName) but I still get the same error in the string.
How could I fix this issue?
This is not proper use of the Shell Method:
Public Shared Function Shell (PathName As String, [...]) As Integer
Parameters
PathName
Type: System.String
Required. String. Name of the program to execute, together with any required arguments and command-line switches. PathName can also include the drive and the directory path or folder.
The first parameter is supposed to be the name of a program to execute. FOR is not a program, it's a built-in feature of the cmd.exe command line interpreter.
As far as I can see, you have the following options:
Option 1: Explicitly call cmd.exe and pass the string that you want to execute with the /c parameter:
Shell("cmd.exe /c for /R %I ...")
Don't for get to duplicate quotation marks (") to escape them.
Option 2: Create a batch file and call the batch file using Shell.
Option 3: Don't use FOR to find the files you need, but use the methods of the System.IO namespace, e.g. Directory.EnumerateFiles, instead.
Escape your internal quote marks like this.
Shell("FOR /R %I in (*.pdf) DO #pdf2swf.exe ""%~fI"" -o ""%~dpI%~nI.swf"" -f -T 9 -t -G ")
As I recall, in VB.Net you escape double quote marks by doubling them.
EDIT:
It might help if you do the iteration outside of the Shell. (Certainly to debug)
Dim sourceFolder As String = "c:\Your call"
Dim sourceFiles As String[] = Directory.GetFiles(sourceFolder, "*.pdf")
ForEach file As String In sourceFiles
Dim justName As String = Path.GetFileNameWithoutExtension(file)
Dim shellCall As String = _
String.Format("pdf2swf.exe ""{0}"" -o ""{1}.swf"" -f -T 9 -t -G", _
file, justName)
Shell(shellCall)
EndFor
You could also cosider using System.Diagnostics.Process instead of Shell
Try escaping the quotes (2 quote marks - "" - in VB.NET, IIRC):
Shell("FOR /R %I in (*.pdf) DO #pdf2swf.exe ""%~fI"" -o ""%~dpI%~nI.swf"" -f -T 9 -t -G ")
If pdf2swf.exe is not in the same folder your program's executable is running from, that could be a reason you're getting the error.
Also, you'll have the same issue with the *.pdf files if they are in a different folder other than where your executable is. You can specify the drive and path to search:
Shell ("FOR /R C:\SomeFolder %I in (*.pdf) DO #pdf2swf.exe ""%~fI"" -o ""%~dpI%~nI.swf"" -f -T 9 -t -G ")

Ghostscript: PDF total pages

I'm using Ghostscript library API (wrapping from C#) to print PDF documents from my application.
With the '-dFirstPage' and '-dLastPage' parameters I'm able to select an range of pages to be printed, but how about the total number of a PDF's pages?
It is not very nice to allow a user to select a page interval from 2 to 10 when, let me say, the PDF document has only 4 pages.
Consider that I'm using Ghostscript library through the gsapi_init_with_args API library call.
Ghostscript can count and display the number of pages of a PDF on stdout. The commandline is
gswin32c ^
-q ^
-dNODISPLAY ^
-c "(input.pdf) (r) file runpdfbegin pdfpagecount = quit"
Here all the -c "..." stuff is a PostScript commandline snippet (using a few GS internal command extensions). And input.pdf is the PDF filename (could also be a full path like (c:/path/to/my.pdf)).
However, a better and faster tool for this kind of job would be to use pdfinfo (part of the XPDF-utilities, also available on Windows).
Update:
#ebyrob wants to know if one can modify my example command line so that it also displays the PDF in a single operation. Try this:
gswin32c ^
-q ^
-c "(input.pdf) (r) file runpdfbegin pdfpagecount =" ^
-f input.pdf
Well, it's not a single operation -- it's just two different operations in a single commandline.
For people having issues in ghostscript >9.50 add --permit-file-read=input.pdf
I tried to make this script:
gswin32c ^
-q ^
-c "(input.pdf) (r) file runpdfbegin pdfpagecount =" ^
-f input.pdf
work in a c# wrapped solution and kept getting error "/undefinedfilename". In this case ensure that your filepath has Slashes "/" as DirectorySeperator and not Backslashes "\". I know Kurt Pfeifle already wrote it, but it happened to me i just overlooked it.
In Windows systems:
"path to gs exec" -q -dNODISPLAY -dNOSAFER --permit-file-read="path to
your file" -c "(""path to your file"") (r) file runpdfbegin
pdfpagecount = quit"
Remarks:
Just change where is 'path to...' with your path, leave the rest as is.
On the -c path you must use double slashes or unix like ones. Ex: C:\\youfile.pdf (good), C:/youfile.pdf (good), C:\yourfile.pdf (bad).
Example:
path: C:\Temp\Some Folder\myFile.pdf
gs path: C:\Temp\Some Folder\gs\bin\gswin64c.exe
path -c 1: C:\\Temp\\Some Folder\\myFile.pdf
path -c 2: C:/Temp/Some Folder/myFile.pdf
Commands:
"C:\Temp\Some Folder\gs\bin\gswin64c.exe" -q -dNODISPLAY -dNOSAFER --permit-file-read="C:\Temp\Some Folder\myFile.pdf" -c "(""C:\\Temp\\Some Folder\\myFile.pdf"") (r) file runpdfbegin pdfpagecount = quit"
"C:\Temp\Some Folder\gs\bin\gswin64c.exe" -q -dNODISPLAY -dNOSAFER --permit-file-read="C:\Temp\Some Folder\myFile.pdf" -c "(""C:/Temp/Some Folder/myFile.pdf"") (r) file runpdfbegin pdfpagecount = quit"
To sum up some of the above separate comments for windows users to avoid needing to alter between / and \\ , to show the total number of pages can be set as a shortcut for drag and drop or "sendTo", by first switching to a working directory.
#echo off & cd /d "%~dp1" & "C:\path to gs\bin\gs.exe" -q --permit-file-read="%~nx1" -c "(%~nx1) (r) file runpdfbegin pdfpagecount = quit" & pause
where gs.exe is one of the windows c(onsole) variants gswin32c.exe or gswin64c.exe
The cd /c "%~dp1" will switch console to quoted file drive path
The full quoted path to "GSwin..c.exe" calls it safely and remotely
-q will suppress (not show) the start message
since version 9.5+ the --permit-file-read="file name" is advised / required
-c "(%~nx1) does not need the quotes for name.xtension
if running a cmd as a shortcut, pause is required to see the result
beware only use on files you trust as your overriding GS -dSAFER restrictions.