Почему текст `fi` обрезается, когда я копирую из PDF или распечатываю документ?


15

Когда я копирую из Adobe Reader файл PDF, который содержит

Define an operation

Я скорее вижу

Dene an operation

когда я вставляю текст, почему это?

Как я могу исправить эту досадную проблему?

Я также видел это в прошлом, когда я печатал файл Microsoft Office Word на своем принтере.

Ответы:


13

Это звучит как проблема со шрифтом. PDF, вероятно, использует fi лигатуру OpenType в слове define, и в текущем шрифте конечного приложения отсутствует этот глиф.

Я не знаю, есть ли простой способ заставить Acrobat разложить лигатуру на копии.

Ваши проблемы с печатью, вероятно, также связаны со шрифтами. Возможно, что-то позволяет принтеру заменять шрифт документа собственными встроенными шрифтами, и в версии шрифта принтера также отсутствует этот конкретный глиф. Чтобы обойти эту проблему, вы должны указать Windows всегда загружать шрифты на принтер.

Другая возможность при печати: UniScribe может быть не включен. MS KB 2642020 рассказывает об этом и о некоторых возможных обходных путях (а именно, использовать печать типа RAW, а не печать EMF). Хотя контекст немного отличается от вашей конкретной проблемы, причина может быть той же, и могут применяться те же обходные пути.


1
Интересно насчет лигатур, мне интересно, можно ли как-то настроить его на правильное поведение. Возможно, я мог бы посмотреть, как ведут себя другие читатели PDF. Где именно настроить его так, чтобы шрифты отправлялись на принтер?
Тамара Вийсман

1
В диалоговом окне печати приложения: нажмите Properties(или Preferences, в зависимости от версии диалогового окна) для принтера, убедитесь, что вы находитесь на вкладке Layoutили Quality, нажмите Advancedкнопку. В Graphicгруппе измените TrueType Fontпараметр на Download as Softfont. Это относится к большинству принтеров и принтеров PostScript, использующих встроенные диалоговые окна Windows (я думаю), но другие драйверы могут что-то перемещать или пропадать.
afrazier

Вы можете найти MS KB 2642020 некоторого использования. Я отредактировал свой ответ с этой информацией.
afrazier

Спасибо за описание проблемы. Я еще не пытался решить эту проблему, но обязательно попробую, когда снова столкнусь с проблемой печати. Я полагаю, что одно из обоих решений наверняка решит эту весьма специфическую проблему ... :)
Тамара Вийсман

@afrazier, решение, которое вы написали в начале комментария "Из диалогового окна печати приложения:", сработало для меня. Я предлагаю вставить этот текст в ваш ответ. (Я мог бы отредактировать его, но я думаю, что решение должно быть за вами.)
Алан

9

Вы можете заменить большинство этих «сломанных» слов оригиналами. Вы можете смело заменить слово, если:

  • как deneили rey, это не настоящее слово
  • как defineили firefly, есть один способ повторно добавить лигатуры sequeneces ( ff, fi, fl, ffi, или ffl) и сделать реальное слово

Большинство проблем с лигатурой соответствуют этим критериям. Однако вы не можете заменить:

  • us потому что это реальное слово, хотя изначально fluffs
    • Кроме того affirm, butterfly, fielders, fortifies, flimflam, misfits...
  • cusпотому что это может стать cuffsилиficus
    • также stiffed/ stifled, rifle/ riffle, flung/ fluffing...

В этом 496 тысяч слов английского словаря , есть 16055 слов , которые содержат , по меньшей мере , один ff, fi, fl, ffi, или ffl, которые превращаются в 15879 слов , когда их лигатуры удаляются. 173 из этих недостающих слов сталкивающихся как cuffsи ficus, а последние 3 являются потому , что словарь содержит слово ff, fiи fl.

790 из этих «удаленных лигатур» слов - это настоящие слова us, но 15089 - это сломанные слова. 14960 сломанных слов можно безопасно заменить исходным словом, что означает, что 99,1% сломанных слов можно исправить, а 93,2% исходных слов, содержащих лигатуру, можно восстановить после вставки копии в PDF. 6,8% слов, содержащих лигатурные последовательности, теряются в collisions ( cus) и подсловах ( us), если вы не выберете какой-либо способ (контекст слова / документа?), Чтобы выбрать лучшую замену для каждого из слов, которые не имеют гарантированного замена.

Ниже приведен мой скрипт на Python, который генерирует приведенную выше статистику. Он ожидает текстовый файл словаря с одним словом в строке. В конце он записывает CSV-файл, который отображает исправляемые сломанные слова на их исходные слова.

Вот ссылка для загрузки CSV: http://www.filedropper.com/brokenligaturewordfixes. Объедините это сопоставление с чем-то вроде сценария замены регулярных выражений, чтобы заменить большинство пропущенных слов.

import csv
import itertools
import operator
import re


dictionary_file_path = 'dictionary.txt'
broken_word_fixes_file_path = 'broken_word_fixes.csv'
ligatures = 'ffi', 'ffl', 'ff', 'fi', 'fl'


with open(dictionary_file_path, 'r') as dictionary_file:
    dictionary_words = list(set(line.strip()
                                for line in dictionary_file.readlines()))


broken_word_fixes = {}
ligature_words = set()
ligature_removed_words = set()
broken_words = set()
multi_ligature_words = set()


# Find broken word fixes for words with one ligature sequence
# Example: "dene" --> "define"
words_and_ligatures = list(itertools.product(dictionary_words, ligatures))
for i, (word, ligature) in enumerate(words_and_ligatures):
    if i % 50000 == 0:
        print('1-ligature words {percent:.3g}% complete'
              .format(percent=100 * i / len(words_and_ligatures)))
    for ligature_match in re.finditer(ligature, word):
        if word in ligature_words:
            multi_ligature_words.add(word)
        ligature_words.add(word)
        if word == ligature:
            break
        # Skip words that contain a larger ligature
        if (('ffi' in word and ligature != 'ffi') or
                ('ffl' in word and ligature != 'ffl')):
            break
        # Replace ligatures with dots to avoid creating new ligatures
        # Example: "offline" --> "of.ine" to avoid creating "fi"
        ligature_removed_word = (word[:ligature_match.start()] +
                                 '.' +
                                 word[ligature_match.end():])
        # Skip words that contain another ligature
        if any(ligature in ligature_removed_word for ligature in ligatures):
            continue
        ligature_removed_word = ligature_removed_word.replace('.', '')
        ligature_removed_words.add(ligature_removed_word)
        if ligature_removed_word not in dictionary_words:
            broken_word = ligature_removed_word
            broken_words.add(broken_word)
            if broken_word not in broken_word_fixes:
                broken_word_fixes[broken_word] = word
            else:
                # Ignore broken words with multiple possible fixes
                # Example: "cus" --> "cuffs" or "ficus"
                broken_word_fixes[broken_word] = None


# Find broken word fixes for word with multiple ligature sequences
# Example: "rey" --> "firefly"
multi_ligature_words = sorted(multi_ligature_words)
numbers_of_ligatures_in_word = 2, 3
for number_of_ligatures_in_word in numbers_of_ligatures_in_word:
    ligature_lists = itertools.combinations_with_replacement(
        ligatures, r=number_of_ligatures_in_word
    )
    words_and_ligature_lists = list(itertools.product(
        multi_ligature_words, ligature_lists
    ))
    for i, (word, ligature_list) in enumerate(words_and_ligature_lists):
        if i % 1000 == 0:
            print('{n}-ligature words {percent:.3g}% complete'
                  .format(n=number_of_ligatures_in_word,
                          percent=100 * i / len(words_and_ligature_lists)))
        # Skip words that contain a larger ligature
        if (('ffi' in word and 'ffi' not in ligature_list) or
                ('ffl' in word and 'ffl' not in ligature_list)):
            continue
        ligature_removed_word = word
        for ligature in ligature_list:
            ligature_matches = list(re.finditer(ligature, ligature_removed_word))
            if not ligature_matches:
                break
            ligature_match = ligature_matches[0]
            # Replace ligatures with dots to avoid creating new ligatures
            # Example: "offline" --> "of.ine" to avoid creating "fi"
            ligature_removed_word = (
                ligature_removed_word[:ligature_match.start()] +
                '.' +
                ligature_removed_word[ligature_match.end():]
            )
        else:
            # Skip words that contain another ligature
            if any(ligature in ligature_removed_word for ligature in ligatures):
                continue
            ligature_removed_word = ligature_removed_word.replace('.', '')
            ligature_removed_words.add(ligature_removed_word)
            if ligature_removed_word not in dictionary_words:
                broken_word = ligature_removed_word
                broken_words.add(broken_word)
                if broken_word not in broken_word_fixes:
                    broken_word_fixes[broken_word] = word
                else:
                    # Ignore broken words with multiple possible fixes
                    # Example: "ung" --> "flung" or "fluffing"
                    broken_word_fixes[broken_word] = None


# Remove broken words with multiple possible fixes
for broken_word, fixed_word in broken_word_fixes.copy().items():
    if not fixed_word:
        broken_word_fixes.pop(broken_word)


number_of_ligature_words = len(ligature_words)
number_of_ligature_removed_words = len(ligature_removed_words)
number_of_broken_words = len(broken_words)
number_of_fixable_broken_words = len(
    [word for word in set(broken_word_fixes.keys())
     if word and broken_word_fixes[word]]
)
number_of_recoverable_ligature_words = len(
    [word for word in set(broken_word_fixes.values())
     if word]
)
print(number_of_ligature_words, 'ligature words')
print(number_of_ligature_removed_words, 'ligature-removed words')
print(number_of_broken_words, 'broken words')
print(number_of_fixable_broken_words,
      'fixable broken words ({percent:.3g}% fixable)'
      .format(percent=(
      100 * number_of_fixable_broken_words / number_of_broken_words
  )))
print(number_of_recoverable_ligature_words,
      'recoverable ligature words ({percent:.3g}% recoverable)'
      '(for at least one broken word)'
      .format(percent=(
          100 * number_of_recoverable_ligature_words / number_of_ligature_words
      )))


with open(broken_word_fixes_file_path, 'w+', newline='') as broken_word_fixes_file:
    csv_writer = csv.writer(broken_word_fixes_file)
    sorted_broken_word_fixes = sorted(broken_word_fixes.items(),
                                      key=operator.itemgetter(0))
    for broken_word, fixed_word in sorted_broken_word_fixes:
        csv_writer.writerow([broken_word, fixed_word])

Ссылка на не .csvработает. Было бы здорово, если бы вы могли загрузить его снова! В любом случае спасибо за код.
MagTun

@Enora Я заново загрузил CSV по той же ссылке - надеюсь, это поможет! Я также заметил несколько проблем в коде / результатах (использование точек в качестве заполнителей, в то время как новый словарь имеет точки в своих словах, а не слова в нижнем регистре перед их сравнением). Я считаю, что все замены правильные, но возьмите их с крошкой соли и знайте, что возможны более хорошие замены. Я рекомендую автоматизировать замены с помощью регулярных выражений, но затем подтвердить, что каждая замена хороша своими глазами.
Ян Ван Брюгген

7

Проблема здесь, как отмечает другой ответ , с лигатурами. Однако это не имеет ничего общего с OpenType. Основная проблема заключается в том, что PDF-файлы представляют собой формат перед печатью, который мало заботится о содержании и семантике, но вместо этого ориентирован на точное представление страницы в том виде, в каком она будет напечатана.

Текст отображается не как текст, а как наборы глифов из шрифта в определенных позициях. Таким образом, вы получите что-то вроде «Поместите глиф № 72 там, глиф № 101 там, глиф № 108 там, ...«. На этом уровне не существует принципиально никакого понятия текста вообще . Это просто описание того, как это выглядит . Есть две проблемы извлечения значения из набора символов:

  1. Пространственная планировка. Поскольку PDF уже содержит конкретную информацию о том, где размещать каждый глиф, фактического текста, лежащего под ним, нет, как обычно. Еще одним побочным эффектом является отсутствие пробелов. Конечно, если вы посмотрите на текст, есть, но не в PDF. Зачем выбрасывать пустой глиф, если вы можете просто ничего не излучать? Результат такой же, в конце концов. Таким образом, читатели PDF должны аккуратно собрать воедино текст, вставляя пробел всякий раз, когда они сталкиваются с большим разрывом между глифами.

  2. PDF отображает глифы, а не текст. Большую часть времени идентификаторы глифов соответствуют кодовым точкам Unicode или, по крайней мере, кодам ASCII во встроенных шрифтах, что означает, что вы часто можете достаточно хорошо вернуть текст ASCII или Latin 1, в зависимости от того, кто создал PDF в первую очередь (некоторые искажать все в процессе). Но часто даже PDF-файлы, которые позволяют вам получать текст ASCII, прекрасно искажают все, что не является ASCII. Особенно ужасно со сложными сценариями, такими как арабский, которые содержат только лигатуры и альтернативные символы после стадии макета, что означает, что арабские PDF-файлы почти никогда не содержат фактического текста

Вторая проблема, как та, с которой вы сталкиваетесь. Распространенным виновником здесь является LaTeX, который использует приблизительное число 238982375 различных шрифтов (каждый из которых ограничен 256 глифами) для достижения своего результата. Различные шрифты для обычного текста, математики (использует более одного) и т. Д. Усложняют ситуацию, особенно потому, что Metafont предшествовал Unicode почти на два десятилетия и, следовательно, никогда не было сопоставления Unicode. Умлауты также отображаются диарезом, наложенным на букву, например, вы получаете »¨a« вместо »ä« при копировании из PDF-файла (и, конечно, также не можете его искать).

Приложения, создающие PDF-файлы, могут включать фактический текст в качестве метаданных. Если этого не произойдет, вы останетесь в зависимости от того, как обрабатываются встроенные шрифты и сможет ли программа чтения PDF снова собрать воедино исходный текст. Но »fi« копирование как пустое или не копирование обычно является признаком LaTeX PDF. Вы должны рисовать символы Unicode на камнях и бросать их производителю, надеясь, что они переключатся на XeLaTeX и, таким образом, наконец, появятся в 1990-х годах в кодировках символов и стандартах шрифтов.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.