Как определить высоту строки в PostScript?

Мне нужно определить высоту строки (в заданном масштабе и шрифте) в postscript.

/Helvetic-Oblique findfont
10 scalefont
setfont
10 10 1 0 360 arc fill
10 10 moveto (test) dup stringwidth pop 2 div neg 0 rmoveto show

будет печатать тест, ориентированный по горизонтали (но еще не вертикально) на (10,10). (чтобы увидеть это, я также покажу небольшой круг на 10,10). Мне также нужно определить высоту строки, чтобы центрировать текст по вертикали, но я не могу найти для нее функцию.

Ответ 1

Вы знакомы с кодом PostScript, который используете? Или это просто слепо скопировано и вставлено откуда-то? Если вы хотите это понять, вам следует обратиться за помощью к "Справочник по языку PostScript" или "Красная книга" или "PLRM". Эти ресурсы доступны в виде PDF файлов от Adobe.

Ваш фрагмент PostScript использует следующие шаги:

  • (test) помещает строку "test" в верхнюю часть стека.
  • dup дублирует самый верхний элемент в стеке. (Теперь у вас будет строка дважды в стеке.)
  • stringwidth. После выполнения этого оператора будет использована самая верхняя строка "тест", а вместо этого будут добавлены два значения: высота строки (верхняя) и ширина строки (вторая сверху). [ Обновление: Собственно, "высота строки" не совсем корректна - скорее, вертикальное смещение текущей точки после окончания рисования строки...]
  • Затем вы используете pop. Это просто удаляет самое верхнее значение в стеке. Теперь в верхней части стека остается только ширина строки.
  • 2 div делит это значение на 2 и оставляет результат (половина строки).
  • neg отрицает самое верхнее значение в стеке. Теперь, когда отрицательное значение является самым верхним в стеке.
  • 0 помещает значение "0" поверх стека.
  • rmoveto затем потребляет два самых верхних значения в стеке и перемещает текущую точку на это расстояние (половина ширины строки) влево.
  • show потребляет первую "тестовую" строку, которая оставалась все время в нижней части стека и "показывает" ее.

Итак, что будет работать, чтобы учесть высоту строки? Попробуйте использовать последнюю строку:

200 700 moveto (test) dup true charpath pathbbox 3 -1 roll sub 2 div neg 3 1 roll sub 2 div exch 200 700 moveto rmoveto show"

Чтобы понять мои изменения, найдите значения charpath, div, exch, pathbbox, roll и sub операторов в Красной книге.

Эта команда использует Ghostscript для создания PDF файла в Windows из кода (проще просмотреть и проверить результаты):

 gswin32c.exe ^
      -o my.pdf ^
      -sDEVICE=pdfwrite ^
      -c "/Helvetic-Oblique findfont 10 scalefont setfont 200 700 1 0 360 arc fill 0 0 moveto (test test) dup true charpath pathbbox 3 -1 roll sub 2 div neg 3 1 roll sub 2 div exch 200 700 moveto rmoveto show"

В Linux используется:

 gs \
      -o my.pdf \
      -sDEVICE=pdfwrite \
      -c "/Helvetic-Oblique findfont 10 scalefont setfont 200 700 1 0 360 arc fill 0 0 moveto (test test) dup true charpath pathbbox 3 -1 roll sub 2 div neg 3 1 roll sub 2 div exch 200 700 moveto rmoveto show"

Более читаемые формы:

  gswin32c ^
     -o my.pdf ^
     -sDEVICE=pdfwrite ^
     -c "/Helvetic-Oblique findfont 10 scalefont setfont" ^
     -c "200 700 1 0 360 arc fill 0 0 moveto (test test) dup" ^
     -c "true charpath pathbbox 3 -1 roll sub 2 div neg 3 1 roll" ^
     -c "sub 2 div exch 200 700 moveto rmoveto show"

и

  gs \
     -o my.pdf \
     -sDEVICE=pdfwrite \
     -c "/Helvetic-Oblique findfont 10 scalefont setfont" \
     -c "200 700 1 0 360 arc fill 0 0 moveto (test test) dup" \
     -c "true charpath pathbbox 3 -1 roll sub 2 div neg 3 1 roll" \
     -c "sub 2 div exch 200 700 moveto rmoveto show"

Ответ 2

Просто добавив к pipitas ответ:

/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

Метод предполагает, что некоторый шрифт уже установлен. Он работает над выбранным шрифтом (setfont) и его размером (scalefont).

Я использую (HÍpg), чтобы получить самый большой ограничивающий прямоугольник, используя подчеркнутые символы верхнего регистра и символы "ниже строки". Результат достаточно хорош.

Альтернативный подход ворует из ответа dreamlax - некоторые шрифты не поддерживают оператор charpath. (См. Как вы можете получить метрику высот в PostScript?)

Сохранение и восстановление графического контекста сохраняет текущую точку на месте, поэтому она не влияет на "поток" вашего документа.

Надеюсь, я помог.

Ответ 3

Здесь ответ на вопрос, чтобы дополнить pipitas подробное объяснение.

Эта процедура позиционирует и показывает строку с центром в указанной точке.

/ceshow { % (string) fontsize fontname x y
    gsave
        moveto findfont exch scalefont setfont % s
        gsave
            dup false charpath flattenpath pathbbox % s x0 y0 x1 y1
        grestore
        3 -1 roll sub % s x0 x1 dy
        3 1 roll sub % s dy -dx
        2 div exch % s -dx/2 dy
        -2 div % s -dx/2 -dy/2
        rmoveto show
    grestore
} bind def

Ответ 4

У меня были ужасные результаты, используя описанные выше процедуры с шрифтом дингбата, и я понял, что они предполагают, что текст действительно начнется с текущей точки, что в некоторых случаях является дико неточным.

Мое решение также полагается на pathbbox для вычисления ширины и высоты, но затем оно также использует X0 и Y0, чтобы сначала получить начало координат.

%-- to make things nicer
/hmoveto { 0 rmoveto } def
/vmoveto { 0 exch rmoveto } def
%-- cshow means something else...
/ccshow {
    dup %-- charpath consumes the string
    gsave
    newpath %-- else there a strange line somewhere
    0 0 moveto
    true charpath flattenpath pathbbox
    grestore
    2 index sub -2 div vmoveto
    2 index sub -2 div hmoveto
    neg vmoveto
    neg hmoveto
    show
} def