Perché il testo `fi` viene tagliato quando copio da un PDF o stampo un documento?


15

Quando copio da un file PDF di Adobe Reader che contiene

Define an operation

Preferisco vedere

Dene an operation

quando incollo il testo, perché?

Come posso risolvere questo fastidioso problema?

Ho anche visto questo accadere in passato quando ho stampato un file di Microsoft Office Word sulla mia stampante.

Risposte:


13

Sembra un problema con i caratteri. Il PDF sta probabilmente usando la fi legatura OpenType nella parola definee al carattere corrente dell'applicazione di destinazione manca quel glifo.

Non so se c'è un modo semplice per convincere Acrobat a scomporre la legatura in copia.

I tuoi problemi con la stampa sono probabilmente anche legati ai caratteri. Qualcosa sta probabilmente permettendo alla stampante di sostituire il carattere del documento con i suoi caratteri incorporati e alla versione della stampante del carattere manca anche quel particolare glifo. Dovresti dire a Windows di scaricare sempre i caratteri sulla stampante per aggirare questo problema.

Un'altra possibilità durante la stampa: UniScribe potrebbe non essere abilitato. 2642020 MS KB parla di questo e di alcune possibili soluzioni alternative (vale a dire, per utilizzare la stampa di tipo RAW anziché la stampa di tipo EMF). Sebbene il contesto sia leggermente diverso dal problema specifico, la causa potrebbe essere la stessa e potrebbero essere applicate le stesse soluzioni alternative.


1
Interessante per le legature, mi chiedo se possa in qualche modo essere configurato per comportarsi correttamente. Forse potrei vedere come si comportano gli altri lettori PDF. Dove lo configuro esattamente in modo che i caratteri vengano inviati alla stampante?
Tamara Wijsman,

1
Dalla finestra di dialogo di stampa di un'app: fai clic su Properties(o Preferences, a seconda della versione della finestra di dialogo) per la stampante, assicurati di essere nelle schede Layouto Quality, fai clic sul Advancedpulsante. Nel Graphicgruppo, cambia l' TrueType Fontopzione in Download as Softfont. Questo riguarda la maggior parte delle stampanti PostScript e le stampanti che utilizzano le finestre di dialogo integrate di Windows (credo), ma altri driver possono spostare le cose o perdere completamente.
Afrazier

Potresti trovare MS KB 2642020 di qualche utilità. Ho modificato la mia risposta con quelle informazioni.
Afrazier

Grazie per aver descritto il problema. Non ho ancora provato a risolverlo, ma ci proverò sicuramente quando incontrerò di nuovo un problema di stampa. Immagino che una di entrambe le soluzioni risolverà sicuramente questo problema molto specifico ... :)
Tamara Wijsman,

@afrazier, la soluzione che hai scritto nel tuo commento a partire da "Dalla finestra di dialogo di stampa di un'app:" ha funzionato per me. Suggerisco di inserire quel testo nella tua risposta. (Potrei modificarlo, ma penso che la decisione dovrebbe essere tua.)
Alan,

9

È possibile sostituire la maggior parte di queste parole "rotte" con gli originali. Puoi tranquillamente sostituire una parola se:

  • come deneo rey, non è una parola vera
  • come defineo firefly, c'è un modo per sequeneces ri-add legatura ( ff, fi, fl, ffi, o ffl) e fare una parola vera e propria

La maggior parte dei problemi di legatura si adattano a questi criteri. Tuttavia, non è possibile sostituire:

  • us perché è una parola vera, anche se in origine avrebbe potuto essere fluffs
    • Inoltre affirm, butterfly, fielders, fortifies, flimflam, misfits...
  • cusperché potrebbe diventare cuffsoficus
    • anche stiffed/ stifled, rifle/ riffle, flung/ fluffing...

In questo dizionario Inglese 496-mille-parola , ci sono 16055 le parole che contengono almeno uno ff, fi, fl, ffi, o ffl, che si trasformano in 15879 parole quando i loro legature vengono rimossi. 173 di quelle parole mancanti in collisione come cuffse ficus, e gli ultimi 3 sono perché questo dizionario contiene le parole ff, fie fl.

790 di queste parole "rimosse dalla legatura" sono parole reali, come us, ma 15089 sono parole spezzate. 14960 delle parole spezzate possono essere tranquillamente sostituite con la parola originale, il che significa che il 99,1% delle parole spezzate sono riparabili e il 93,2% delle parole originali che contengono una legatura può essere recuperato dopo aver incollato un PDF. Il 6,8% delle parole contenenti sequenze di legature vanno perse a causa delle collisioni ( cus) e delle parole secondarie ( us), a meno che non si scelga un modo (contesto di parole / documenti?) Per scegliere la migliore sostituzione per ciascuna delle parole che non hanno una garanzia sostituzione.

Di seguito è riportato il mio script Python che ha generato le statistiche di cui sopra. Si aspetta un file di testo del dizionario con una parola per riga. Alla fine scrive un file CSV che mappa le parole spezzate riparabili alle loro parole originali.

Ecco un link per scaricare CSV: http://www.filedropper.com/brokenligaturewordfixes Combina questa mappatura con qualcosa come uno script di sostituzione regex per sostituire la maggior parte delle parole rotte.

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

Il collegamento a .csvè interrotto. Sarebbe fantastico se si potesse caricare di nuovo! In ogni caso, grazie per il codice.
MagTun,

@Enora Ho ricaricato il CSV allo stesso link - spero che sia d'aiuto! Ho anche notato alcuni problemi nel codice / nei risultati (usando i punti come segnaposto mentre il nuovo dizionario ha punti nelle sue parole e non mettere in minuscolo le parole prima di confrontarle). Credo che tutti i ricambi siano corretti, ma li prendo con un granello di sale e so che sono possibili sostituzioni più buone. Consiglio di automatizzare le sostituzioni con regex ma poi confermare che ogni sostituzione è buona con i tuoi occhi.
Jan Van Bruggen,

7

Il problema qui è, come nota l' altra risposta , con le legature. Tuttavia, non ha nulla a che fare con OpenType. Il problema fondamentale è che i PDF sono un formato di prestampa che si occupa solo di contenuti e semantica, ma è invece orientato a rappresentare fedelmente una pagina come verrebbe stampata.

Il testo non è strutturato come testo ma come una serie di glifi da un carattere in determinate posizioni. Quindi ottieni qualcosa del tipo »Inserisci il numero glifo 72 lì, il glifo numero 101 lì, il glifo numero 108 lì, ...«. A quel livello non c'è fondamentalmente alcuna nozione di testo a tutti . È solo una descrizione di come appare . Esistono due problemi nell'estrarre il significato da un gruppo di glifi:

  1. La disposizione spaziale. Poiché il PDF contiene già informazioni specifiche su dove posizionare ciascun glifo, non vi è alcun testo reale sottostante come sarebbe normale. Un altro effetto collaterale è che non ci sono spazi. Certo, se guardi il testo ci sono, ma non nel PDF. Perché emettere un glifo vuoto quando non puoi emetterne affatto? Il risultato è lo stesso, dopo tutto. Quindi i lettori PDF devono ricomporre attentamente il testo, inserendo uno spazio ogni volta che incontrano uno spazio maggiore tra i glifi.

  2. Il PDF esegue il rendering di glifi, non di testo. Il più delle volte gli ID glifi corrispondono ai punti di codice Unicode o almeno ai codici ASCII nei caratteri incorporati, il che significa che spesso è possibile recuperare abbastanza bene il testo ASCII o Latino 1, a seconda di chi ha creato il PDF in primo luogo (alcuni rovinare tutto nel processo). Ma spesso anche i PDF che ti permettono di tirar fuori il testo ASCII bene rovineranno tutto ciò che non è ASCII. Soprattutto orribile con script complessi come l'arabo che contengono solo legature e glifi alternativi dopo la fase di layout, il che significa che i PDF arabi non contengono quasi mai testo effettivo

Il secondo problema è come quello che devi affrontare. Un colpevole comune qui è LaTeX che utilizza un numero stimato di 238982375 caratteri diversi (ognuno dei quali è limitato a 256 glifi) per ottenere il suo output. Caratteri diversi per testo normale, matematica (ne usa più di uno), ecc. Rendono le cose molto difficili, soprattutto perché Metafont precede Unicode di quasi due decenni e quindi non c'è mai stata una mappatura Unicode. Le Umlaut sono anche rese da una diaeresi sovrapposta a una lettera, ad esempio si ottiene »¨a« invece di »ä« quando si copia da un PDF (e ovviamente non si può nemmeno cercare).

Le applicazioni che producono PDF possono scegliere di includere il testo effettivo come metadati. In caso contrario, rimani in balia di come vengono gestiti i caratteri incorporati e se il lettore PDF può ricomporre il testo originale. Ma "fi" essere copiato come vuoto o per niente è di solito un segno di un PDF LaTeX. Dovresti dipingere i personaggi Unicode su pietre e lanciarli al produttore, sperando che passino a XeLaTeX e arrivino così finalmente negli anni '90 alla codifica dei caratteri e agli standard dei caratteri.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.