Is there a way via PostScript to add a string such that is will be truncated by "..." so as not to exceed a certain width?
I've looking at some old report generation code and would like add this feature. In the existing reports, values that are too long are visually overwriting other data.
The reason I'm trying to do this at the PS level is that in the existing code I don't see anything that could calculate any kind of accurate width metric.
I've yet to write any Postscript, so maybe this is trivial. (?)
Per comment below: Yes, localization will an issue. So I guess a user defined "ellipsis" string makes sense.
Here is some example output that shows how strings are currently printed:
% Change font style and/or size
/Times-Roman-ISOLatin1 findfont 12 scalefont setfont
219 234 moveto (AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA_) show
Can this be modified to ellipsize things?
Well you can do something like this (replace the char in front of concatstrings with your ellipsis):
/concatstrings % (a) (b) -> (ab)
{ exch dup length
2 index length add string
dup dup 4 2 roll copy length
4 -1 roll putinterval
} bind def
/ellipsis_show {
1 dict begin
/width_t exch def
{dup stringwidth pop width_t lt {exit} if dup length 1 sub 0 exch getinterval} loop
(_) concatstrings
show
end
}def
% Change font style and/or size
/Times-Roman-ISOLatin1 findfont 12 scalefont setfont
0 0 moveto (foobar barfoo foofoo barbar) 100.0 ellipsis_show
concatstrings copied from: http://en.wikibooks.org/wiki/PostScript_FAQ#How_to_concatenate_strings.3F
The simple answer is 'no'. A longer answer is that, since PostScript is a programming language, you can do this, but it will require some knowledge of PostScript, and some work, it certainly is not trivial.
You can redefine the various operators which draw text on the output, there are quite a few; show, ashow, cshow, kshow, xshow, yshow, xyshow, widthshow, awidthshow, and glyphshow. You could define modified versions of these which determine (using stringwidth and the parameters used by the various operators) the width of thefinal printed text. Probably you would want to calculate this glyph by glyph and terminate with your ellipsis when the value exceeds some threshold. (NB not all fonts will contain an ellipsis glyph, and its encoded position may vary).
However, given that you are working with existing code, there is most probably already a function defined to draw text and it probably only uses a subset of the possible operators. You would probably be better advised to modify that.
Related
I have been trying to decode a pdf file using python and the data is as below:
BT
/F2 8.8 Tf
1 0 0 1 36.85 738.3 Tm
0 g
0 G
[(A)31(c)-44(c)-44(o)-79(u)11(n)-79(t)5( )] TJ
ET
How do I make sense of this???
[(A)31(c)-44(c)-44(o)-79(u)11(n)-79(t)5( )] is of what type???
BT /F2 8.8 Tf 1 0 0 1 36.85 738.3 Tm 0 g 0 G [(A)31(c)-44(c)-44(o)-79(u)11(n)-79(t)5( )] TJ ET
Is normal plain ASCII text, thus everyday, decoded binary as text.
Your question is
Q) How do I make sense of this??? [(A)31(c)-44(c)-44(o)-79(u)11(n)-79(t)5( )]
A) Always look at the context
BT = B(egin) T(ext)
/F2 = use F(ont) 2 for encoding (whatever that is)
8.8 = units of height (if un-modified those could be 8.8 full unscaled DTP points,
but beware, point size does not necessarily correspond to any measurement
of the size of the letters on the printed page.)
... Mainly T(ransform )m(atrix) e.g. placement
[ = start a string group
(A) = literal(y) "A"
31 = Kern next character (+ is usually) left wise by 31 units where units (is usually) 1/1440 inch or 17.639 µm
(c) = the next glyph literal that needs to be etched on screen or paper
-44 is push the two x (c) apart by 44 units
(c)
...
] Tj ET = Close Group, T(exte)j(ect) E(nd) T(ext)
So there we have it somewhere on the page (first or last word or any time in between) but at that time somewhere, most likely top Left, there is one continuous selectable plain text string that **audibly sounds like a word in a human language = "Account", with an extra spacebar literal (that's actually un-necessary for a PDF, it will print that and any other "word" good enough without one.)
Why did I say sounds and not "looks like" is because those "literal" characters are not the ones presented they are the encoded names of glyphs.
Hear :-) is how they could look like using /F2 if it was set to different glyph font such as use emojis or other Dings, so A is BC but c is a checkbox u is underground t is train but audibly all ink, is just an Account of which graphics to use.
I'm writing a tool for working with tiled images. One feature is to convert a whole image into a tileset and tilemap, e.g. a 160x144px image would have a set of unique 8x8 tiles and a 20x18 map of tile IDs.
The next goal is to support palettes. On some of the older platforms that used tiled graphics, you might have 8 palettes of 4 colors each, or 16 of 16 each. I want to automatically create a set of palettes that fits within the N-by-K limit, using as few palettes as possible; and assign those palettes to the tilemap, or alert if it's not possible.
There are some obvious first steps: if any single tile uses more than K colors, it won't be possible; and once that's been checked, any tile whose colors are a subset of another can trivially share its palette. The difficulty is handling partially overlapping palettes. Consider 17 tiles, each with 15 unique colors; if there's enough overlap, they can fit within 16x16 color palettes, but it might not be possible.
I expect a dynamic programming solution would work here. At any stage in the problem, one has a partial assignment of tiles to palettes; and the decision is to which of the N palettes to assign the next tile. (The tile might not even have any colors in common with the optimal choice of palette at that time; consider 4 tiles each with 4 unique colors, they could all use a single 16-color palette.)
Has this particular problem been solved already? Is there a known algorithm for it, or just the general tips of all dynamic programming?
SuperFamiconv is capable of doing this for a few systems, including SNES (16 palettes, 8 colors/palette) and GBC (8 palettes, 4 colors/palette). It's also open-source, so their algorithm is available.
It turns out that dynamic programming isn't necessary for a "good enough" solution given realistically-sized images. (Not sure how well it would do for huge ones, but it doesn't matter for my purposes.)
This is a translation of their algorithm to Python:
def optimize_palettes(tilepals, N, K):
"""
Return an optimized palette set for the given tile set.
tilepals -- A list of sets of unique colors used for each tile of the image
N -- The maximum number of palettes for one image
K -- The maximum number of colors for one tile palette
"""
# Check that each tilepal fits within the color limit
if any(len(s) > K for s in tilepals):
raise OverflowError, "A tile has more than %d unique colors" % K
# Remove duplicate tilepals
sets = []
for s in tilepals:
if s not in sets:
sets.append(s)
# Remove tilepals that are proper subsets of other tilepals
sets = [s for s in sets if not any(c != s and s.issubset(c) for c in sets)]
# Sort tilepals from most to fewest colors
sets.sort(key=len, reverse=True)
# Combine tilepals as long as they fit within the color limit
opt = []
for s in sets:
for cs in opt:
if len(s | cs) <= K:
cs.update(s)
break
else:
opt.append(s)
# Sort tilepals from most to fewest colors
opt.sort(key=len, reverse=True)
# Check that the palettes fit within the palette limit
if len(opt) > N:
raise OverflowError, "There are more than %d palettes" % N
return opt
I'm using Go and compiling it to web assembly.
I'm trying to render a bunch of rectangles next to eachother with a random colour, but they keep rendering as just gray.
My render function looks something like this:
for row,_ := range rows {
for col,_ := range row {
ctx.Set("fillStyle", fmt.Sprintf("#%06x", rand.Int()))
ctx.Call("fillRect", 20, 20 + (col * width), maxHeight - (row*height))
}
}
With which it renders a big block (all rectangles are next to eachother) but just all in gray, instead of doing them in different colours.
Is this enough code in the example to help further? If not I can post it to a gist, as I'm new to WASM I'm unsure which parts could really be relevant - but those 2 functions are the only ones doing something with rendering as far as I can tell.
The problem is that you use this expression to construct the fill style:
fmt.Sprintf("#%06x", rand.Int())
rand.Int() returns a non-negative pseudo-random int. Size of int is 64 bits if GOOS=js and GOARCH=wasm. What this means is that the random int number will be random 8 bytes (first bit being always 0 due to being non-negative).
If you format such a number with the %06x verb, like almost all the time it will be more than just 6 hex digits. The width 6 means to be at least 6, and the flag 0 means to pad with zeros if less. But if it's longer, it is not truncated.
And if you set an invalid color to canvas.fillStyle, it will disregard it and the last set valid fill style will remain active. And I'm guessing it was a gray color you used before the loop.
Fix is easy, just make sure the random number has no more than 3 bytes, or in other words, 6 hex digits. Use a simple bitmask:
ctx.Set("fillStyle", fmt.Sprintf("#%06x", rand.Int()&0xffffff))
Or use rand.Intn() instead of rand.Int():
ctx.Set("fillStyle", fmt.Sprintf("#%06x", rand.Int(0x1000000)))
Also context.fillRect() expects 4 arguments: x, y, width and height, so it should be something like this:
ctx.Call("fillRect", 20+(col*width), maxHeight-(row*height), width, height)
Can someone help me understand this "Rocky Mountain BASIC" or "HTBasic" code?
I have to find out why the print functionality doesn't work anymore.
First, this line
PRINTER IS 26
I understand that the printer that we are going to use is "26" but what does 26 mean?
REPEAT
IF LWC$(Imp$)="o" THEN
PRINTER IS 26
FOR I=0 TO VAL(Mesu$(0,5))
FOR L=0 TO 6
PRINT Mesu$(I,L)
NEXT L
NEXT I
ELSE
FOR L=0 TO 6
PRINT TABXY(2,9+L);Mesu$(0,L)
NEXT L
FOR C=1 TO VAL(Mesu$(0,5))
PRINT TABXY(20-36*(C>3)+(C-1)*12,8+8*(C>3)),"voie "&VAL$(C-1)
FOR L=1 TO 7
PRINT TABXY(20-36*(C>3)+(C-1)*12,L+8+8*(C>3)),Mesu$(C,L-1)
NEXT L
NEXT C
END IF
INPUT "SORTIE sur l'IMPRIMANTE O/N ?",Imp$
UNTIL LWC$(Imp$)="n"
“26” is one of the codes that specifies an output port for the PRINT statement. For example,
PRINTER IS CRT
PRINTER IS PRT
The letter codes correspond to number codes; PRINTER IS CRT is the same as PRINTER IS 1, for example, and PRT is the same as 701.
The codes that are likely to work for printing in this BASIC dialect, including 26, are:
26 701 9 15 19 23 24 25
I pulled this from an ancient document, Using HP BASIC For Instrument Control: A Self-Study Course, which you may find useful. (I suspect you meant HPBasic, not HTBasic, in your subject line?)
TABXY is a variant of the PRINT statement, for printing to specific locations on a CRT screen; the docs I’m seeing say that the XY is ignored if not printing to a CRT, but I wouldn’t be surprised if TABXY also worked on some plotters. The first two numbers would be the X and Y coordinates to begin displaying the text, with TABXY(1, 1) indicating the upper left corner, and the lower right corner depending on how many columns and rows the CRT has.
You may find the HP9000 series BASIC Language Reference, Volume 1 and BASIC Language Reference, Volume 2 useful.
LWC$ is just a lowercase function, to ensure that that whether the user inputs “O”, “N”, “o”, or “n” at the INPUT line, the program will respond correctly.
VAL converts a string to the number that that string represents. The string “3” would become the number 3, for example.
The variables Mesu$ is likely a two-dimensional array, with x from 0 to, judging from line 4, a variable amount contained in Mesu$(0, 5) and y from 0 to 6, judging from line 5.
I guess that the line with PLOTTER IS 26 and we say that we want colors.
MAT Menu$=("")
DISP "envoi à l' imprimante .."
Menu$(1)="PLOTTER"
Menu$(2)="IMPRIMANTE COULEUR"
!Select(0,1,Tp,26,12,1)
IF Tp=1 THEN
PLOTTER IS 705,"HPGL"
ELSE
PLOTTER IS 26,"HPGL;PCL5;COLOR,1600",0,260,0,185
END IF
You can obtain the width of a string in the current font with stringwidth and although this actually pushes offset coordinates on the stack, the y-value always seems to be useless. Is there a way to determine the exact height of a string, that may or may not include descenders?
stringwidth, as it says, doesn't return string's height. (In all cases I looked at, the second integer on the stack after executing stringwidth was 0 -- for strings that run in horizontal direction.) stringwidth gives the relative coordinates of the currentpoint after executing a (string) show.
The PLRM has this to say about stringwidth:
Note that the width returned by stringwidth is defined as movement of the current
point. It has nothing to do with the dimensions of the glyph outlines.
So what would work to take into account the string's height? The magic words to read up about in PRLM are charpath and pathbbox. Try this:
%!
/Helvetica findfont 60 scalefont setfont
200 700 4 0 360 arc fill
200 700 moveto (test test) dup
true charpath pathbbox
3 -1 roll sub 2 div neg 3 1 roll sub 2 div exch
1 0 0 setrgbcolor
200 700 moveto rmoveto show showpage
It calculates the string's (printed in red) height and uses that info to try and center a small filled circle (printed in black) into the center of its bounding box:
I have already answered this in How to determine string height in PostScript?, but it is useful here also.
Just adding to pipitas answer:
/textheight {
gsave % save graphic context
{
100 100 moveto % move to some point
(HÍpg) true charpath pathbbox % gets text path bounding box (LLx LLy URx URy)
exch pop 3 -1 roll pop % keeps LLy and URy
exch sub % URy - LLy
}
stopped % did the last block fail?
{
pop pop % get rid of "stopped" junk
currentfont /FontMatrix get 3 get % gets alternative text height
}
if
grestore % restore graphic context
} bind def
/jumpTextLine {
textheight 1.25 mul % gets textheight and adds 1/4
0 exch neg rmoveto % move down only in Y axis
} bind def
The method expects that some font is already set. It works over the selected font (setfont) and its size (scalefont).
I use (HÍpg) to get the biggest bounding box possible, using accentuated uppercase characters and "below line" characters. The result is good enough.
The alternative approach steals from dreamlax's answer -- some fonts do not support charpath operator.
Saving and restoring the graphic context keeps the current point in place, so it has no impact over the "flow" of your document.
Hope I've helped.
This seems to work most of the time:
/fontheight { currentfont /FontMatrix get 3 get } bind def
/lineheight { fontheight 1.2 mul } bind def
It won't work for all /FontTypes.