Как определить высоту строки в 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). Мне также нужно определить высоту строки, чтобы центрировать текст по вертикали, но я не могу найти функцию для нее.

4 ответа

Решение

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

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

  1. (test) помещает строку "тест" на вершину стека.
  2. dup дублирует самый верхний элемент в стеке. (Теперь у вас будет строка в стеке дважды.)
  3. stringwidth, После выполнения этого оператора будет использована самая верхняя "тестовая" строка, и вместо этого в стек будут добавлены два значения: высота строки (самая верхняя) и ширина строки (вторая сверху). [Обновление: На самом деле, "высота строки" не совсем корректна - это скорее вертикальное смещение текущей точки после окончания рисования строки...]
  4. Далее вы используете pop, Это просто удаляет самое верхнее значение в стеке. Теперь только ширина строки остается на вершине стека.
  5. 2 div делит это значение на 2 и оставляет результат (половину ширины строки).
  6. neg отменяет самое верхнее значение в стеке. Теперь это отрицательное значение является самым верхним в стеке.
  7. 0 помещает значение "0" на вершину стека.
  8. rmoveto затем использует два верхних значения в стеке и перемещает текущую точку на это расстояние (половину ширины строки) влево.
  9. 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"

Просто добавив к ответу 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?)

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

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

Вот краткий ответ, чтобы дополнить подробное объяснение Пипитаса.

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

/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

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

Мое решение также опирается на 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's 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
Другие вопросы по тегам