Pourquoi le texte «fi» est-il coupé lorsque je copie à partir d'un PDF ou imprime un document?

15

Lorsque je copie à partir d'un fichier PDF Adobe Reader contenant

Define an operation

Je vois plutôt

Dene an operation

quand je colle le texte, pourquoi?

Comment puis-je remédier à ce problème ennuyeux?

J'ai également vu cela se produire dans le passé lorsque j'ai imprimé un fichier Microsoft Office Word sur mon imprimante.

Tamara Wijsman
la source

Réponses:

13

Cela ressemble à un problème de police. Le PDF utilise probablement la fi ligature OpenType dans le mot define, et la police actuelle de l'application de destination ne contient pas ce glyphe.

Je ne sais pas s'il existe un moyen simple d'obtenir qu'Acrobat décompose la ligature sur copie.

Vos problèmes d'impression sont probablement également liés à la police. Quelque chose permet probablement à l'imprimante de remplacer la police du document par ses propres polices intégrées et la version de l'imprimante de la police manque également ce glyphe particulier. Vous devez dire à Windows de toujours télécharger les polices sur l'imprimante pour contourner ce problème.

Autre possibilité lors de l'impression: UniScribe n'est peut-être pas activé. MS KB 2642020 parle de cela et de certaines solutions de contournement possibles (à savoir, utiliser l'impression de type RAW plutôt que l'impression de type EMF). Bien que le contexte soit légèrement différent de votre problème spécifique, la cause peut être la même et les mêmes solutions de contournement peuvent s'appliquer.

afrazier
la source
1
Intéressant sur les ligatures, je me demande s'il peut en quelque sorte être configuré pour se comporter correctement. Je pourrais peut-être voir comment les autres lecteurs PDF se comportent. Où dois-je le configurer pour que les polices soient envoyées à l'imprimante?
Tamara Wijsman
1
À partir de la boîte de dialogue d'impression d'une application: cliquez sur Properties(ou Preferences, selon la version de la boîte de dialogue) pour l'imprimante, assurez-vous que vous êtes sur les onglets Layoutou Quality, cliquez sur le Advancedbouton. Dans le Graphicgroupe, changez l' TrueType Fontoption en Download as Softfont. Cela couvre la plupart des imprimantes PostScript et des imprimantes utilisant les boîtes de dialogue intégrées de Windows (je pense), mais d'autres pilotes peuvent avoir des choses déplacées ou carrément manquantes.
afrazier
Vous pouvez trouver MS KB 2642020 d'une certaine utilité. J'ai modifié ma réponse avec cette information.
afrazier
Merci d'avoir décrit le problème. Je n'ai pas encore essayé de résoudre ce problème, mais j'essaierai certainement lorsque je rencontrerai à nouveau un problème d'impression. Je suppose que l'une des deux solutions résoudrait certainement ce problème très spécifique ... :)
Tamara Wijsman
@afrazier, la solution que vous avez écrite dans votre commentaire commençant par "À partir de la boîte de dialogue d'impression d'une application:" a fonctionné pour moi. Je suggère de mettre ce texte dans votre réponse. (Je pourrais le modifier, mais je pense que la décision devrait revenir à vous.)
Alan
9

Vous pouvez remplacer la plupart de ces mots "cassés" par les originaux. Vous pouvez remplacer un mot en toute sécurité si:

  • comme deneou reyce n'est pas un vrai mot
  • comme defineou firefly, il y a un moyen de ligatures ajouter à nouveau sequeneces ( ff, fi, fl, ffiou ffl) et faire un vrai mot

La plupart des problèmes de ligature répondent à ces critères. Cependant, vous ne pouvez pas remplacer:

  • us parce que c'est un vrai mot, même s'il aurait pu à l'origine être fluffs
    • aussi affirm, butterfly, fielders, fortifies, flimflam, misfits...
  • cuscar il pourrait devenir soit cuffsouficus
    • aussi stiffed/ stifled, rifle/ riffle, flung/ fluffing...

Dans ce dictionnaire anglais 496 mille mots , il y a, 16055 mots qui contiennent au moins un ff, fi, fl, ffiou ffl, qui se transforment en 15879 mots quand sont retirés leurs ligatures. 173 de ces mots manquants sont entrés en collision comme cuffset ficus, et le dernier 3 sont parce que le dictionnaire contient les mots ff, fiet fl.

790 de ces mots "sans ligature" sont de vrais mots us, mais 15089 sont des mots cassés. 14960 des mots cassés peuvent être remplacés en toute sécurité par le mot d'origine, ce qui signifie que 99,1% des mots cassés sont réparables et 93,2% des mots originaux qui contiennent une ligature peuvent être récupérés après avoir copié-collé un PDF. 6,8% des mots contenant des séquences de ligatures sont perdus dans les collisions ( cus) et sous-mots ( us), à moins que vous ne choisissiez un moyen (contexte de mot / document?) De choisir le meilleur remplacement pour chacun des mots qui n'ont pas de garantie remplacement.

Ci-dessous mon script Python qui a généré les statistiques ci-dessus. Il attend un fichier texte de dictionnaire avec un mot par ligne. À la fin, il écrit un fichier CSV qui mappe les mots cassés réparables à leurs mots d'origine.

Voici un lien pour télécharger le CSV: http://www.filedropper.com/brokenligaturewordfixes Combinez ce mappage avec quelque chose comme un script de remplacement regex afin de remplacer la plupart des mots cassés.

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])
Jan Van Bruggen
la source
Le lien vers le .csvest rompu. Ce serait génial si vous pouviez le télécharger à nouveau! Dans tous les cas, merci pour le code.
MagTun
@Enora J'ai re-téléchargé le CSV sur le même lien - j'espère que ça aide! J'ai également remarqué quelques problèmes dans le code / résultats (en utilisant des points comme espaces réservés tandis que le nouveau dictionnaire a des points dans ses mots, et pas en minuscules avant de les comparer). Je crois que tous les remplacements sont corrects, mais prenez-les avec un grain de sel et sachez que d'autres bons remplacements sont possibles. Je recommande d'automatiser les remplacements avec regex mais confirmer ensuite que chaque remplacement est bon de vos propres yeux.
Jan Van Bruggen
7

La question est ici, comme les autres réponses notes, avec ligatures. Cependant, cela n'a rien à voir avec OpenType. Le problème fondamental est que les PDF sont un format de pré-impression qui ne se préoccupe que peu du contenu et de la sémantique, mais est plutôt destiné à représenter fidèlement une page telle qu'elle serait imprimée.

Le texte est présenté non pas comme du texte mais comme des séquences de glyphes d'une police à certaines positions. Donc, vous obtenez quelque chose comme »Placez le glyphe numéro 72 là, le glyphe numéro 101 là, le glyphe numéro 108 là, ...«. Sur ce niveau il n'y a fondamentalement aucune notion de texte du tout . C'est juste une description à quoi ça ressemble . Il y a deux problèmes pour extraire le sens d'un tas de glyphes:

  1. La disposition spatiale. Étant donné que le PDF contient déjà des informations spécifiques sur l'emplacement de chaque glyphe, aucun texte réel ne le sous-tend comme cela serait normal. Un autre effet secondaire est qu'il n'y a pas d'espace. Bien sûr, si vous regardez le texte, il y en a, mais pas dans le PDF. Pourquoi émettre un glyphe vierge alors que vous pouvez simplement n'en émettre aucun? Le résultat est le même, après tout. Les lecteurs PDF doivent donc reconstituer soigneusement le texte, en insérant un espace chaque fois qu'ils rencontrent un écart plus important entre les glyphes.

  2. PDF affiche des glyphes, pas du texte. La plupart du temps, les ID de glyphe correspondent aux points de code Unicode ou au moins aux codes ASCII dans les polices incorporées, ce qui signifie que vous pouvez souvent récupérer assez bien le texte ASCII ou Latin 1, selon la personne qui a créé le PDF en premier lieu (certains tout brouiller dans le processus). Mais souvent, même les fichiers PDF qui vous permettent d'extraire très bien du texte ASCII vont tout gâcher tout ce qui n'est pas ASCII. Particulièrement horrible avec des scripts complexes tels que l'arabe qui ne contiennent que des ligatures et des glyphes alternatifs après l'étape de mise en page, ce qui signifie que les PDF arabes ne contiennent presque jamais de texte réel

Le deuxième problème est comme celui auquel vous êtes confronté. Un coupable commun ici est LaTeX qui utilise un nombre estimé de 238982375 polices différentes (dont chacune est limitée à 256 glyphes) pour obtenir sa sortie. Différentes polices pour le texte normal, les mathématiques (en utilise plusieurs), etc. rendent les choses très difficiles, d'autant plus que Metafont est antérieur à Unicode de près de deux décennies et qu'il n'y a donc jamais eu de mappage Unicode. Les trémas sont également rendus par une tréma superposée à une lettre, par exemple, vous obtenez »¨a« au lieu de »ä« lors de la copie à partir d'un PDF (et bien sûr, vous ne pouvez pas non plus la rechercher).

Les applications qui produisent des PDF peuvent choisir d'inclure le texte réel en tant que métadonnées. S'ils ne le font pas, vous êtes laissé à la merci de la façon dont les polices intégrées sont gérées et si le lecteur PDF peut reconstituer le texte d'origine. Mais »fi« étant copié en blanc ou pas du tout est généralement le signe d'un PDF LaTeX. Vous devez peindre des caractères Unicode sur des pierres et les jeter sur le producteur, en espérant qu'ils passeront à XeLaTeX et arriveront enfin dans les années 1990 d'encodages de caractères et de normes de police.

Joey
la source