Чому текст `fi` розрізається, коли я копіюю з PDF або друкую документ?


16

Коли я копіюю PDF-файл Adobe Reader, який містить

Define an operation

Я швидше бачу

Dene an operation

коли я вставляю текст, чому це?

Як можна виправити цю дратівливу проблему?

Я також бачив це в минулому, коли я друкував файл Microsoft Office Word на своєму принтері.

Відповіді:


14

Це звучить як проблема з шрифтом. PDF, ймовірно, використовує fi лігатуру OpenType у слові define, а в поточному шрифті програми призначення відсутній цей гліф.

Я не знаю, чи існує простий спосіб змусити Acrobat розкласти лігатуру на копії.

Можливо, ваші проблеми з друком також пов'язані з шрифтом. Щось, ймовірно, дозволяє принтеру замінити шрифт документа власними вбудованими шрифтами, а версія шрифту принтера також не вистачає саме цього гліфа. Вам доведеться сказати Windows завжди завантажувати шрифти на принтер, щоб вирішити цю проблему.

Інша можливість при друку: UniScribe може бути не включена. MS KB 2642020 розповідає про це та деякі можливі способи вирішення (а саме: використання друку типу RAW, а не друку типу ЕРС). Хоча контекст дещо інший, ніж ваша конкретна проблема, причина може бути однаковою, і можуть застосовуватися ті самі шляхи вирішення.


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% слів, що містять послідовності лігатури, втрачаються зіткненнями ( 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 за тим же посиланням - сподіваюся, що це допоможе! Я також помітив декілька проблем у коді / результатах (використовуючи періоди в якості заповнювачів, тоді як у новому словнику є слова з періодами, а не порівняння слів перед їх порівнянням). Я вважаю, що всі заміни є правильними, але прийміть їх із зерном солі та знаю, що можливі більш якісні заміни. Я рекомендую автоматизувати заміни з регулярним виразом, але потім підтвердити кожну заміну добре своїми очима.
Ян Ван Брюгген

8

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

Текст викладається не як текст, а як відбітки гліфів від шрифту на певних позиціях. Отже, ви отримуєте щось на кшталт «Помістіть там глиф число 72, номер глифа 101, там число 108». На цьому рівні не існує принципово ніякого поняття тексту взагалі . Це просто опис, як це виглядає . Існує дві проблеми з вилученням значення з купки гліфів:

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

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

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

Програми, що створюють PDF, можуть вибрати фактичний текст як метадані. Якщо цього немає, ви залишаєтесь на милість того, як обробляються вбудовані шрифти та чи зможе зчитувач PDF знову зібрати оригінальний текст. Але "fi", копіюється як порожній або взагалі не є ознакою PDF-документа LaTeX. Ви повинні намалювати символи Unicode на каміння та кинути їх на продюсера, сподіваючись, що вони перейдуть на XeLaTeX і, нарешті, наблизяться до 90-х кодувань символів та стандартів шрифту.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.