Search code examples
postscriptword-wrapellipsis

How to wrap or put ellipsis on text using postscript?


I have a postscript code that accepts input of text. I want to wrap the text or add ellipsis on it when it reaches a specific length but I don't know how to do it.

%%% A4 size
<< /PageSize [595 842] >> setpagedevice
clippath pathbbox /ury exch def /urx exch def /lly exch def /llx exch def

%%% Unit
/cm { 72 2.54 div mul } bind def

%%% Temporary
/Fix_long 4.2 cm def
/Fix_short 3.2 cm def
%%% Set Image Scale
/SetFixScale { 2 copy gt { Fix_long Fix_short }{ Fix_short Fix_long  }ifelse scale } bind def

%%% Set put coordinate
/SetXAdjust { 2 copy gt 
{ X_Step Fix_long sub 2 div floor }
{ Fix_long Fix_short sub 2 div} ifelse /XAdjust exch def 
} bind def
 /YAdjust 2 cm def
%%% Temporary
/Row 4 def
/Column 3 def
/X_Step urx llx sub Row div floor def
/Y_Step ury lly sub Column div floor  def
/Row_pos 0 def
/Column_pos 1 def
/SetPutPosition { 
llx X_Step Row_pos mul add 
ury Y_Step Column_pos mul sub translate
DrawFrame
DrawFileName
XAdjust YAdjust translate
Row 1 sub Row_pos eq { /Row_pos 0 def /Column_pos Column_pos 1 add def }{ /Row_pos Row_pos 1 add def } ifelse  
Column_pos Column gt { /Column_pos 1 def } if
} bind def

/DrawFrame { gsave .5 setlinewidth 0 0 X_Step Y_Step rectstroke grestore } bind def
/DrawFileName { 15 15 moveto 4 -1 roll show } bind def

in the /DrwaFileName part, I want to wrap the text/cut it and add an elipsis. How can I do it? is there a function for it?


Solution

  • There's no built-in function for this, so you'll have to do some string processing. There are at least two methods depending on how you'd like to measure "length".

    For the simpler case, we'll consider "length" to be the count of characters. The postscript operator length will return the number of characters in a string. So starting with your function,

    /DrawFileName { 15 15 moveto 4 -1 roll   show } bind def
    %                                      ^
    % we'll insert our code here, ---------|
    % where the string is on top of the stack,
    % replacing the call to show
    

    first check the existing length against the maximum size we want to allow.

    dup length 10 gt {
    }{
        show
    } ifelse
    

    If the length is not greater than 10, the else-clause simply calls show. Next we'll fill-in the then-clause to trim the string.

    dup length 10 gt {
        0 10 getinterval
        show
    }{
        show
    } ifelse
    

    Hmm, I didn't intend to talk about optimizing, but we can factor this right now. Before adding the ellipsis part. Both cases call show, so we can simply call it once outside of the ifelse structure.

    dup length 10 gt {
        0 10 getinterval
    }{
    } ifelse
    show
    

    And of course, now we don't need ifelse because the else-clause is empty.

    dup length 10 gt {
        0 10 getinterval
    } if
    show
    

    That's better. Now for ellipsis, we can use the putinterval operator to copy from one string to another.

    dup length 10 gt {
        0 10 getinterval
        dup 7 (...) putinterval
    } if
    show
    

    Edit: Of course, since postscript is a graphics language, we might instead be interested in a string's typeset length on the page. For that, there's the stringwidth operator which returns the amount that the currentpoint will move after calling show. So if you were to simply replace a call to show with stringwidth rmoveto, it would leave a gap of the appropriate size, but not set any text. So, back to the beginning, replacing a call to show, ie. assuming there's a string on the top of stack at the start of our code fragment.

    dup stringwidth pop 50 gt {
    } if
    show
    

    For western alphabets, the dy value returned by stringwidth will always be 0. Since the context is a filename, this seems like a safe assumption here. So we simply pop it and compare the dx result with 50 user-space points.

    Now, what if it's too long? Where to make the cut? That's something to think about. Make a thinking face. Hmmph. If we consider that we're also adding an ellipsis here, we want to subtract that from the field-width and chop the string just to the left of that. So my plan here is to loop through the string character-by-character and check and show each one, leaving the ellipsis string on the stack for that final show. Here's a partial filling-out.

    dup stringwidth pop 50 gt {      % str
        50 (...) stringwidth pop sub % str Max      new maximum length
        exch                         % Max str
        { % Max c                               forall yields the integer value
            ( ) dup 0 4 3 roll put   % Max (c)    put it back in a string
            dup stringwidth pop      % Max (c) dx 
            2 index 2 copy lt        % Max (c) dx Max dx<Max?
            {                        % Max (c) dx Max      dx<Max
            }{                       % Max (c) dx Max      dx>=Max
            } ifelse
        } forall
        (...)
    } if
    show
    

    Loop through each character, check the stringwidth, compare against max. If less than max, we want to go ahead and show the string, and subtract the width from max. If not we want to clean up the stack and exit the loop.

    dup stringwidth pop 50 gt {      % str
        50 (...) stringwidth pop sub % str Max      new maximum length
        exch                         % Max str
        { % Max c                               forall yields the integer value
            ( ) dup 0 4 3 roll put   % Max (c)    put it back in a string
            dup stringwidth pop      % Max (c) dx 
            2 index 2 copy lt        % Max (c) dx Max dx<Max?
            {                        % Max (c) dx Max      dx<Max
                exch sub             % Max (c) Max-dx
                3 1 roll exch pop    % Max-dx (c)
                show                 % Max-dx
            }{                       % Max (c) dx Max      dx>=Max
                pop pop pop pop exit
            } ifelse
        } forall
        (...)   % place (...) on stack for show
    } if
    show
    

    And that should do the job, I think.

    For wrapping text, we've kind of painted ourselves into a corner. The remainder of the string would need to be collected by the remaining iterations, and then reassembled into a string, before doing the whole mess all over again. Let me give that some thought. ...

    Or it could re-position the currentpoint in the middle of the loop. Using an X coordinate and a Y increment (or decrement, really, because we're moving down the page, probably). Something like

    currentpoint exch pop % currentY
    X exch                % X currentY
    dY sub                % X currentY-dY
    moveto