Tại sao văn bản `fi` bị cắt khi tôi sao chép từ PDF hoặc in tài liệu?


15

Khi tôi sao chép từ tệp Adobe Reader PDF có chứa

Define an operation

Tôi thà thấy

Dene an operation

Khi tôi dán văn bản, tại sao lại thế này?

Làm thế nào tôi có thể khắc phục vấn đề gây phiền nhiễu này?

Tôi cũng đã thấy điều này xảy ra trong quá khứ khi tôi in tệp Microsoft Office Word sang máy in của mình.

Câu trả lời:


13

Điều này nghe có vẻ như một vấn đề phông chữ. PDF có thể đang sử dụng chữ fi ghép OpenType trong từ definevà phông chữ hiện tại của ứng dụng đích bị thiếu glyph đó.

Tôi không biết có cách nào dễ dàng để Acrobat phân hủy dây chằng trên bản sao không.

Vấn đề của bạn với in ấn có lẽ cũng liên quan đến phông chữ. Một cái gì đó có thể cho phép máy in thay thế phông chữ của tài liệu bằng phông chữ tích hợp của chính nó và phiên bản phông chữ của máy in cũng thiếu glyph cụ thể đó. Bạn phải yêu cầu Windows luôn tải phông chữ xuống máy in để khắc phục sự cố này.

Một khả năng khác khi in: UniScribe có thể không được bật. MS KB 2642020 nói về điều này và một số cách giải quyết có thể (cụ thể là sử dụng in kiểu RAW thay vì in kiểu EMF). Mặc dù bối cảnh hơi khác so với vấn đề cụ thể của bạn, nguyên nhân có thể giống nhau và cách giải quyết tương tự có thể được áp dụng.


1
Thú vị về các chữ ghép, tôi tự hỏi nếu nó bằng cách nào đó có thể được cấu hình để hành xử đúng. Có lẽ tôi có thể nhìn cách các độc giả PDF khác cư xử. Chính xác thì tôi định cấu hình nó ở đâu để các phông chữ được gửi đến máy in?
Tamara Wijsman

1
Từ hộp thoại in của ứng dụng: Nhấp Properties(hoặc Preferences, tùy thuộc vào phiên bản hộp thoại) cho máy in, đảm bảo bạn đang ở trên Layouthoặc Qualitycác tab, nhấp vào Advancednút. Trong Graphicnhóm, thay đổi TrueType Fonttùy chọn thành Download as Softfont. Điều này bao gồm hầu hết các máy in và máy in PostScript sử dụng các hộp thoại tích hợp trong Windows (tôi nghĩ), nhưng các trình điều khiển khác có thể khiến mọi thứ bị di chuyển hoặc mất tích.
afrazier

Bạn có thể tìm thấy MS KB 2642020 của một số sử dụng. Tôi đã chỉnh sửa câu trả lời của mình với thông tin đó.
afrazier

Cảm ơn đã mô tả vấn đề. Tôi chưa thử giải quyết vấn đề này nhưng chắc chắn sẽ thử khi gặp lại sự cố in. Tôi đoán một trong hai giải pháp chắc chắn sẽ giải quyết được vấn đề rất cụ thể này ... :)
Tamara Wijsman

@afrazier, giải pháp bạn đã viết trong phần bình luận bắt đầu "Từ hộp thoại in của ứng dụng:" đã làm việc cho tôi. Tôi đề nghị đưa văn bản đó vào câu trả lời của bạn. (Tôi có thể chỉnh sửa nó, nhưng tôi nghĩ rằng quyết định sẽ tùy thuộc vào bạn.)
Alan

9

Bạn có thể thay thế hầu hết các từ "bị hỏng" này bằng bản gốc. Bạn có thể thay thế một từ một cách an toàn nếu:

  • thích denehay không rey, nó không phải là một từ thực sự
  • như definehoặc firefly, có một cách để sequeneces tái add dấu gạch nối ( ff, fi, fl, ffi, hoặc ffl) và làm cho một từ thực

Hầu hết các vấn đề về dây chằng phù hợp với các tiêu chí này. Tuy nhiên, bạn không thể thay thế:

  • us bởi vì nó là một từ thực sự, mặc dù ban đầu nó có thể là fluffs
    • cũng affirm, butterfly, fielders, fortifies, flimflam, misfits...
  • cusbởi vì nó có thể trở thành cuffshoặcficus
    • cũng stiffed/ stifled, rifle/ riffle, flung/ fluffing...

Trong 496 ngàn từ trong từ điển tiếng Anh này , có 16.055 từ mà chứa ít nhất một ff, fi, fl, ffi, hay ffl, mà biến thành 15.879 từ khi ligature họ bị xóa. 173 của những lời thiếu va chạm như cuffsficus, và cuối cùng 3 là vì từ điển có chứa các từ ff, fifl.

790 trong số những từ "loại bỏ dây chằng" này là những từ thực sự, giống như us, nhưng 15089 là những từ bị hỏng. 14960 từ bị hỏng có thể được thay thế một cách an toàn bằng từ gốc, có nghĩa là 99,1% từ bị hỏng có thể sửa được và 93,2% từ gốc có chứa một chữ ghép có thể được phục hồi sau khi sao chép PDF. 6,8% từ có chứa chuỗi chữ ghép bị mất cho các va chạm ( cus) và từ phụ ( us), trừ khi bạn chọn một cách nào đó (ngữ cảnh từ / tài liệu?) Để chọn từ thay thế tốt nhất cho mỗi từ không được bảo đảm thay thế.

Dưới đây là tập lệnh Python của tôi đã tạo ra số liệu thống kê ở trên. Nó mong đợi một tệp văn bản từ điển với một từ trên mỗi dòng. Cuối cùng, nó viết một tệp CSV ánh xạ các từ bị hỏng có thể sửa thành các từ gốc của chúng.

Đây là một liên kết để tải xuống CSV: http://www.filedropper.com/brokenligaturewordfixes Kết hợp ánh xạ này với một cái gì đó giống như một tập lệnh thay thế regex để thay thế hầu hết các từ bị hỏng.

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])

Các liên kết đến .csvbị hỏng. Điều đó sẽ thật tuyệt nếu bạn có thể tải nó lên một lần nữa! Trong mọi trường hợp, cảm ơn cho mã.
MagTun

@Enora Tôi đã tải lên lại CSV ở cùng một liên kết - hy vọng nó có ích! Tôi cũng nhận thấy một vài vấn đề trong mã / kết quả (sử dụng dấu chấm làm trình giữ chỗ trong khi từ điển mới có dấu chấm trong từ và không hạ thấp từ trước khi so sánh chúng). Tôi tin rằng tất cả các thay thế là chính xác, nhưng hãy dùng chúng với một hạt muối và biết rằng có thể thay thế tốt hơn. Tôi khuyên bạn nên tự động hóa việc thay thế bằng regex nhưng sau đó xác nhận từng thay thế là tốt bằng mắt của bạn.
Jan Van Bruggen

6

Vấn đề ở đây là, như các câu trả lời khác ghi chú, với chữ ghép. Tuy nhiên, nó không có gì để làm với OpenType. Vấn đề cơ bản là các tệp PDF là một định dạng in sẵn chỉ quan tâm rất ít đến nội dung và ngữ nghĩa mà thay vào đó là hướng đến việc đại diện trung thực cho một trang như nó sẽ được in.

Văn bản được đặt ra không phải là văn bản mà là chạy glyphs từ một phông chữ tại các vị trí nhất định. Vì vậy, bạn nhận được một cái gì đó như »Đặt glyph số 72 ở đó, glyph số 101 ở đó, glyph số 108 ở đó, ...«. Ở cấp độ đó về cơ bản không có khái niệm nào về văn bản cả . Nó chỉ là một mô tả như thế nào nó trông . Có hai vấn đề trích xuất ý nghĩa từ một loạt các glyphs:

  1. Bố cục không gian. Vì PDF đã chứa thông tin cụ thể nơi đặt từng glyph, không có văn bản thực sự bên dưới nó như bình thường. Một tác dụng phụ khác là không có không gian. Chắc chắn, nếu bạn nhìn vào văn bản có, nhưng không có trong PDF. Tại sao lại phát ra một glyph trống khi bạn hoàn toàn không thể phát ra? Kết quả là như nhau, sau tất cả. Vì vậy, người đọc PDF phải cẩn thận ghép lại văn bản một lần nữa, chèn một khoảng trống bất cứ khi nào họ gặp phải một khoảng cách lớn hơn giữa các glyphs.

  2. PDF biểu hiện glyphs, không phải văn bản. Hầu hết thời gian ID glyph tương ứng với các điểm mã Unicode hoặc ít nhất là mã ASCII trong các phông chữ được nhúng, điều đó có nghĩa là bạn thường có thể lấy lại văn bản ASCII hoặc Latin 1, tùy thuộc vào người đã tạo PDF ở vị trí đầu tiên (một số cắt xén mọi thứ trong quá trình). Nhưng thường thì ngay cả các tệp PDF cho phép bạn lấy ra văn bản ASCII cũng sẽ xử lý mọi thứ không phải là ASCII. Đặc biệt khủng khiếp với các tập lệnh phức tạp như tiếng Ả Rập chỉ chứa chữ ghép và chữ tượng hình xen kẽ sau giai đoạn bố cục, điều đó có nghĩa là các tệp tiếng Ả Rập hầu như không bao giờ chứa văn bản thực tế

Vấn đề thứ hai giống như vấn đề bạn phải đối mặt. Một thủ phạm phổ biến ở đây là LaTeX sử dụng số lượng ước tính 238982375 phông chữ khác nhau (mỗi phông được giới hạn ở 256 glyphs) để đạt được đầu ra. Các phông chữ khác nhau cho văn bản thông thường, toán học (sử dụng nhiều hơn một), v.v. làm cho mọi thứ trở nên rất khó khăn, đặc biệt là khi Metafont có trước Unicode gần hai thập kỷ và do đó không bao giờ có ánh xạ Unicode. Umlauts cũng được hiển thị bằng một dấu gạch chéo được đặt trên một chữ cái, ví dụ: bạn nhận được »¨a« thay vì »ä« khi sao chép từ PDF (và tất nhiên không thể tìm kiếm nó).

Các ứng dụng sản xuất PDF có thể chọn bao gồm văn bản thực tế dưới dạng siêu dữ liệu. Nếu không, bạn sẽ không biết cách xử lý các phông chữ nhúng và liệu trình đọc PDF có thể ghép lại văn bản gốc một lần nữa hay không. Nhưng »fi« được sao chép dưới dạng trống hoặc không hoàn toàn thường là dấu hiệu của LaTeX PDF. Bạn nên vẽ các ký tự Unicode trên đá và ném chúng vào nhà sản xuất, hy vọng chúng sẽ chuyển sang XeLaTeX và do đó cuối cùng đã đến thập niên 1990 về mã hóa ký tự và tiêu chuẩn phông chữ.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.