Don't try EPS, but use specialized software to do this. There are specialized programs such as GraphViz, but even Microsoft Excel should be able to draw such simple bar graphs.
You cannot use any and all fonts in an EPS – this one uses Calibri-Bold, which works dandy with Photoshop and Illustrator, but my InDesign fails to recognize it and only shows squares. (If you have a similar problem, try something like /Helvetica-Bold
.)
All texts must be entered using the standard PostScript StandardEncoding; you cannot use UTF8 or any of your own system's encodings, and accented characters must be entered in escaped form. You cannot use kerning, or ligatures, or any other OpenType feature. You cannot enter text in Arabic, Hebrew, Chinese, Japanese, or Devanagari (added to prevent a few comments on regarding the same). While it's theoretically possible to devise a format to allow for basic text formatting (bold, italic, underline, super- and subscript), this is very hard to do, as it would require far more advanced string parsing.
Nevertheless ... it was a blast to go and try this in pure PostScript; always fun to exercise those brain muscles. I have added lots of comments throughout and tried to put all 'redefinable' stuff together. I suppose you would want the inserting and editing of the actual values to be as simple as possible, therefore I made it possible to insert them straight into the code, not as an array or with predefined delimiters. It's a simple format: each line holds a 'year' (it can actually be any label) and the value it should display. The bar values need to be integers only, and PostScript cannot handle very large integers, so keep an eye out for these.
You can change a lot of parameters; font, size, colors, bar width and max length, and a parameter biasWidth
which, when set to something else than the default 0
, gets subtracted from each value. Thus, you can display a range with, say, values such as 1200
and 1210
with a bias of 1190
, so the two bars would be 10
and 20
units wide instead.
There is a problem with creating EPSes this way: there is no way to know in advance how large the graph is going to be, but the Bounding Box needs to be set in advance, at the top of the file, in a comment. Better make it large enough or your graphs will be clipped.
The code for this EPS comes pretty close to your original graph. You can immediately place it into InDesign (although it will be slow!), and import it in other software such as Photoshop and Illustrator if you want to manually add design.
Enjoy :)
%!PS-Adobe-2.0 EPSF-2.0
%%BoundingBox: 0 0 1018 966
% Note that 'officially' the BoundingBox cannot be set other than to a
% reasonable value -- we don't know at this point! And you cannot set it 'afterwards'
% first, a few convenience functions
% RGB values in PostScript are decimal fractions of 0..1
% so you might prefer 'whole' values in the range 0..255 instead.
% 0 0 255 setRGB -> dark blue
/setRGB {
255 div 3 -1 roll 255 div 3 -1 roll 255 div 3 -1 roll setrgbcolor
} def
% this is the max width of your longest bar
/bar_scale 823 def
% value makebar -> create a path with the width set to value (biased and scaled)
% this uses fontSize to calculate the vertical offset to center the texts (more or less)
% -- the 0.7 is a reasonable assumption for the "cap height" of regular fonts
/makebar {
biasWidth sub
0 barThickness fontSize 0.7 mul add 2 div rmoveto
0 barThickness neg rlineto
bar_scale mul 0 rlineto
0 barThickness rlineto
closepath
} def
% value prettyNumber -> show value as thousands separated string
% note that this routine *cannot* handle floating point numbers
% nor negative value, nor too large values
/prettyNumber {
/val exch cvi def
% show each next thousands separated part
/padZeros false def
val 1000000 ge {
/padZeros true def
val 1000000 idiv =string cvs show
(,) show
/val val 1000000 mod def
} if
padZeros {
val 100000 lt {
(0) show
} if
val 10000 lt {
(0) show
} if
val 1000 idiv =string cvs show
(,) show
/val val 1000 mod def
} {
val 1000 ge {
/padZeros true def
val 1000 idiv =string cvs show
(,) show
/val val 1000 mod def
} if
} ifelse
padZeros {
val 100 lt {
(0) show
} if
val 10 lt {
(0) show
} if
} if
val =string cvs show
} def
% [year value] -> display complete bar, 'year' then bar with number
/bar {
dup 0 get /year exch def
1 get /value exch def
% show the 'year' string in its own color
yearColor setRGB
year =string cvs show
currentpoint /y exch def /x exch 23 add def
% this is to make a number 'peek out' in black if the bar is shorter than its value
x 21 add y moveto
0 0 0 setRGB
value prettyNumber
% set color for the bar
value max eq {
maxColor setRGB
} {
barColor setRGB
} ifelse
newpath
x y moveto
value makebar
fill
x y moveto
value makebar
gsave
clip
inBarColor setRGB
x 21 add y moveto
value prettyNumber
grestore
} def
% end of the convenience functions
% per-graph values come below
% this is the size of all texts
/fontSize 35 def
% this is your font definition
/Calibri-Bold findfont fontSize scalefont setfont
% title at the top
/title (GROWTH IN PRICE PER SQM BETWEEN 2008 AND 2018) def
% color of the title; all colors are in RGB
/titleColor { 33 51 101 } def
% the color of the year at the left
/yearColor { 33 51 101 } def
% the color of the number inside the bars
/inBarColor { 192 255 255 } def
% the color of regular bars
/barColor { 52 98 175 } def
% the color of the Maximum bar
/maxColor { 61 188 161 } def
% the value to bias the bar widths with --
% this gets subtracted from the value before calculating the bar width
% Do NOT make it larger than the smallest bar! This code will *possibly* handle negative numbers ... weirdly
% in case of doubt, set it to 0 (this will accurately scale all bars)
/biasWidth 0 def
% bar thickness -- best set to a value larger than your font size
/barThickness 59 def
% inter-bar spacing
/barSpacing 12 def
% let's define your values inline
% first put everything into an array until END is encountered
% you can add blank lines -- but don't accidentally insert spaces on these!
[
{
{ currentfile 100 string readline
{ dup () eq
{ pop }
{dup (END) eq
{ pop exit } if
} ifelse
} { exit } ifelse
} loop
} exec
2018 48799
2017 41335
2016 41401
2015 36001
2014 37179
2013 31750
2012 29791
2011 31260
2010 28173
2009 24538
2008 28342
END
] /values exch def
% convert to a double array for easier access
[
values {
[ exch
token {
exch token pop exch pop
} if
]
} forall
] /values exch def
% then show it!
/max values 0 get 1 get def
values {
1 get
dup max
gt {
/max exch def
} {
pop
} ifelse
} forall
% we re-define bar_scale here to a fraction
/bar_scale bar_scale max biasWidth sub div def
% get the top position for the title, and top-down display
% the bars
/ypos values length barSpacing barThickness add mul 10 add def
% fetch a 'year' string to get its height and width
% so the title can be placed at the bar start position
values 0 get 0 get
=string cvs stringwidth pop
47 add 23 add ypos 92 add moveto
titleColor setRGB
title show
% and all that's left is to show all of the values!
values {
47 ypos moveto
bar
/ypos ypos barSpacing sub barThickness sub def
} forall
%%EOF
The result, as rasterized by Photoshop:

I should add that my PostScript was rusty enough to need the help of the PostScript Reference Manual by Paul Bourke, lots of Googling for code samples, but especially the excellent xpost
PostScript interpreter on https://tio.run/#postscript-xpost, written by Stack Overflow member luser droog.