As promised, here is my Cairo version. I wrote it using Lua, using lfs to go around directories. I like these little problems, as they allow me to explore the APIs that I have long wanted to dig ...
lfs and LuaCairo are cross-platform, so it should work on other systems (tested on French WinXP Pro SP3).
I made the drawing file names of the first version as I walked through the tree. Advantage: no memory overhead. Inconvenience: I have to specify the size of the image in advance, so lists are likely to be disabled.
So, I made this version by first breaking through the directory tree, saving it in the Lua table. Then, knowing the number of files, creating a canvas so that it fits (at least vertically) and draws the names.
You can easily switch between PNG and SVG rendering. The problem with the latter: Cairo generates it at a low level, drawing letters instead of using the SVG text feature. Well, at the very least, this ensures accurate breaks even on non-font systems. But the files are bigger ... Not a problem if you compress it to have a .svgz file.
Or it should not be too difficult to create SVG directly, I used Lua to generate SVG in the past.
-- LuaFileSystem <http://www.keplerproject.org/luafilesystem/> require"lfs" -- LuaCairo <http://www.dynaset.org/dogusanh/> require"lcairo" local CAIRO = cairo local PI = math.pi local TWO_PI = 2 * PI --~ local dirToList = arg[1] or "C:/PrgCmdLine/Graphviz" --~ local dirToList = arg[1] or "C:/PrgCmdLine/Tecgraf" local dirToList = arg[1] or "C:/PrgCmdLine/tcc" -- Ensure path ends with / dirToList = string.gsub(dirToList, "([^/])$", "%1/") print("Listing: " .. dirToList) local fileNb = 0 --~ outputType = 'svg' outputType = 'png' -- dirToList must have a trailing slash function ListDirectory(dirToList) local dirListing = {} for file in lfs.dir(dirToList) do if file ~= ".." and file ~= "." then local fileAttr = lfs.attributes(dirToList .. file) if fileAttr.mode == "directory" then dirListing[file] = ListDirectory(dirToList .. file .. '/') else dirListing[file] = "" end fileNb = fileNb + 1 end end return dirListing end --dofile[[../Lua/DumpObject.lua]] -- My own dump routine local dirListing = ListDirectory(dirToList) --~ print("\n" .. DumpObject(dirListing)) print("Found " .. fileNb .. " files") --~ os.exit() -- Constants to change to adjust aspect local initialOffsetX = 20 local offsetY = 50 local offsetIncrementX = 20 local offsetIncrementY = 12 local iconOffset = 10 local width = 800 -- Still arbitrary local titleHeight = width/50 local height = offsetIncrementY * (fileNb + 1) + titleHeight local outfile = "CairoDirTree." .. outputType local ctxSurface if outputType == 'svg' then ctxSurface = cairo.SvgSurface(outfile, width, height) else ctxSurface = cairo.ImageSurface(CAIRO.FORMAT_RGB24, width, height) end local ctx = cairo.Context(ctxSurface) -- Display a file name -- file is the file name to display -- offsetX is the indentation function DisplayFile(file, bIsDir, offsetX) if bIsDir then ctx:save() ctx:select_font_face("Sans", CAIRO.FONT_SLANT_NORMAL, CAIRO.FONT_WEIGHT_BOLD) ctx:set_source_rgb(0.5, 0.0, 0.7) end -- Display file name ctx:move_to(offsetX, offsetY) ctx:show_text(file) if bIsDir then ctx:new_sub_path() -- Position independent of latest move_to -- Draw arc with absolute coordinates ctx:arc(offsetX - iconOffset, offsetY - offsetIncrementY/3, offsetIncrementY/3, 0, TWO_PI) -- Violet disk ctx:set_source_rgb(0.7, 0.0, 0.7) ctx:fill() ctx:restore() -- Restore original settings end -- Increment line offset offsetY = offsetY + offsetIncrementY end -- Erase background (white) ctx:set_source_rgb(1.0, 1.0, 1.0) ctx:paint() --~ ctx:set_line_width(0.01) -- Draw in dark blue ctx:set_source_rgb(0.0, 0.0, 0.3) ctx:select_font_face("Sans", CAIRO.FONT_SLANT_NORMAL, CAIRO.FONT_WEIGHT_BOLD) ctx:set_font_size(titleHeight) ctx:move_to(5, titleHeight) -- Display title ctx:show_text("Directory tree of " .. dirToList) -- Select font for file names ctx:select_font_face("Sans", CAIRO.FONT_SLANT_NORMAL, CAIRO.FONT_WEIGHT_NORMAL) ctx:set_font_size(10) offsetY = titleHeight * 2 -- Do the job function DisplayDirectory(dirToList, offsetX) for k, v in pairs(dirToList) do --~ print(k, v) if type(v) == "table" then -- Sub-directory DisplayFile(k, true, offsetX) DisplayDirectory(v, offsetX + offsetIncrementX) else DisplayFile(k, false, offsetX) end end end DisplayDirectory(dirListing, initialOffsetX) if outputType == 'svg' then cairo.show_page(ctx) else --cairo.surface_write_to_png(ctxSurface, outfile) ctxSurface:write_to_png(outfile) end ctx:destroy() ctxSurface:destroy() print("Found " .. fileNb .. " files")
Of course you can change the styles. I did not draw communication lines, I did not see it as needed. I can add them later.