Domanda

Ho un'applicazione che genera metafiles (EMF). Utilizza il dispositivo di riferimento (alias lo schermo) per rendere questi metafiles, quindi il DPI del metafile cambia a seconda della macchina su cui il codice è in esecuzione.

Diciamo

  • Un rclframe di {0, 0, 21590, 27940} (dimensioni del metafile, in millesimi di un mm)
  • A Szldevice di {1440, 900} (dimensioni del dispositivo di riferimento, in pixel)
  • A szlmillimeters di {416, 260} (dimensioni del dispositivo di riferimento, in mm)

Ok, quindi il rclframe mi dice che le dimensioni dell'EMF dovrebbero essere

  • 21590 /2540 = 8,5 in largo
  • 27940 /2540 = 11 in alto

Proprio su. Usando queste informazioni, possiamo determinare anche il DPI fisico del mio monitor, se la mia matematica è giusta:

  • (1440 * 25.4) / 416 = 87.9231 DPI orizzontale
  • (900 * 25.4) / 260 = 87.9231 DPI verticale

Il problema

Tutto ciò che riproduce questo metafile-una conversione EMF-a-PDF, la pagina "Riepilogo" quando fai clic con il pulsante destro del mouse sull'EMF in Windows Explorer, ecc.-PERSONA per troncare il valore DPI calcolato, visualizzando 87 anziché 87.9231 (anche 88 andrebbe bene).

Ciò si traduce in una pagina di dimensioni fisiche come 8,48 in x 10,98 in (usando 87 dpi) anziché 8,5 in x 11 in (usando 88 dpi) quando il metafile viene riprodotto.

  • È possibile modificare il DPI del dispositivo di riferimento in modo che le informazioni memorizzate nel metafile utilizzate per calcolare il DPI vengano fuori a un bel numero?
  • Posso creare il mio contesto del dispositivo e specificare il suo DPI? O devo davvero usare una stampante per farlo?

Grazie per qualsiasi intuizione.

È stato utile?

Soluzione

Sono curioso di sapere come Windows conosca le dimensioni fisiche del tuo monitor. Devi aver modificato una configurazione da qualche parte? Forse puoi cambiarlo in valori più convenienti che si dividono bene.

Come implicito dal nome, un "contesto del dispositivo" deve essere collegato a un dispositivo di sistema. Tuttavia, questo non deve essere un driver hardware, potrebbe essere un emulatore di dispositivi come un driver di stampa di scrittore PDF. Ne ho visto almeno uno che ti consente di impostare un DPI arbitrario.

Altri suggerimenti

Ora ho imparato più di quanto mi importasse di sapere sui metafiles.

1. Alcuni dei Metafile I sovraccarichi di costruttore di Class funzionano male e opereranno con un valore DPI troncato.

Considera quanto segue:

protected Graphics GetNextPage(SizeF pageSize)
{
    IntPtr deviceContextHandle;
    Graphics offScreenBufferGraphics;
    Graphics metafileGraphics;
    MetafileHeader metafileHeader;

    this.currentStream = new MemoryStream();
    using (offScreenBufferGraphics = Graphics.FromHwnd(IntPtr.Zero))
    {
        deviceContextHandle = offScreenBufferGraphics.GetHdc();
        this.currentMetafile = new Metafile(
            this.currentStream,
            deviceContextHandle,
            new RectangleF(0, 0, pageSize.Width, pageSize.Height),
            MetafileFrameUnit.Inch,
            EmfType.EmfOnly);

        metafileGraphics = Graphics.FromImage(this.currentMetafile);

        offScreenBufferGraphics.ReleaseHdc();
    }

    return metafileGraphics;
}

Se sei passato in un SizeF di {8.5, 11}, potresti aspettarti di ottenere un Metafile che ha un rclFrame di {21590, 27940}. La conversione di centimetri in millimetri non è difficile, dopo tutto. Ma probabilmente non lo farai. A seconda della risoluzione, GDI+, a quanto pare, utilizzerà un valore DPI troncato durante la conversione del parametro di pollici. Per farlo bene, devo farlo da solo in centesimi di un millimetro, che GDI+ passa solo poiché è così che è conservato in modo nativo nell'intestazione del Metafile:

this.currentMetafile = new Metafile(
    this.currentStream,
    deviceContextHandle,
    new RectangleF(0, 0, pageSize.Width * 2540, pageSize.Height * 2540),
    MetafileFrameUnit.GdiCompatible,
    EmfType.EmfOnly);

Errore di arrotondamento n. 1 Risolto-il rclFrame del mio metafile è ora corretto.

2. Il DPI su a Graphics Registrazione dell'istanza a a Metafile è sempre sbagliato.

Guarda quello metafileGraphics variabile che ho impostato chiamando Graphics.FromImage() Sul metafile? Beh, sembra che quello Graphics L'istanza avrà sempre un DPI di 96 dpi. (Se dovessi indovinare, è sempre impostato su logico Dpi, non il fisico uno.)

Puoi immaginare quell'ilarità che ne deriva quando stai attingendo a Graphics istanza che opera sotto 96 dpi e registrando a Metafile istanza che ha 87.9231 dpi "registrato" nella sua testata. (Dico "registrato" perché è calcolato dagli altri valori.) I "pixel" del metafile (ricorda, i comandi GDI memorizzati nel metafile sono specificati in Pixel) sono più grandi, e quindi maledizioni e mormora perché la tua chiamata per disegnare qualcosa di lungo un pollice finisce per essere una lunghezza di un pollice.

La soluzione è ridimensionare il Graphics esempio:


metafileGraphics = Graphics.FromImage(this.currentMetafile);
metafileHeader = this.currentMetafile.GetMetafileHeader();
metafileGraphics.ScaleTransform(
    metafileHeader.DpiX / metafileGraphics.DpiX,
    metafileHeader.DpiY / metafileGraphics.DpiY);

Non è un colpo? Ma sembra funzionare.

L'errore #2 "arrotondato" è stato risolto-quando dico di disegnare qualcosa a "1 pollice" a 88 dpi, che Pixel è stato meglio essere $%$^! Registrato come Pixel #88.

3. szlMillimeters può variare selvaggiamente; Il desktop remoto provoca molto divertimento.

Quindi, abbiamo scoperto (per risposta di Mark) che, a volte, Windows chiede l'EDID del tuo monitor e sa davvero quanto sia grande fisicamente. GDI+ usa utile questo (HORZSIZE ecc) quando si riempie szlMillimeters proprietà.

Ora immagina di andare a casa per eseguire il debug di questo codice di desktop remoto. Supponiamo che il tuo computer di casa abbia un monitor Widescreen 16: 9.

Ovviamente, Windows non può interrogare l'EDID di un display remoto. Quindi usa l'antica impostazione predefinita di 320 x 240 mm, il che andrebbe bene, tranne per il fatto che è un rapporto 4: 3 di proporzione, e ora lo stesso codice è generare un metafile su un display che presumibilmente non ha non Pixel fisici quadrati: il DPI orizzontale e il DPI verticale sono diversi e non ricordo l'ultima volta che l'ho visto accadere.

La mia soluzione alternativa per questo per ora è: "Beh, non eseguirla sotto il desktop remoto".

4. Lo strumento EMF-a-PDF che stavo usando ha avuto un errore di arrotondamento quando guardavo il rclFrame intestazione.

Questa è stata la causa principale del mio problema che ha innescato questa domanda. Il mio Metafile era "corretto" da sempre (beh, corretto dopo aver risolto i primi due problemi) e tutta questa ricerca di creazione di un metafile "ad alta risoluzione" era un'aringa rossa. È vero che una certa fedeltà viene persa quando si registra il metafile su un dispositivo di visualizzazione a bassa risoluzione; Questo perché i comandi GDI specificati nel metafile sono specificati nei pixel. Non importa che si tratti di un formato vettoriale e può aumentare o scendere, alcune informazioni vengono perse Durante la registrazione effettiva Quando GDI+ decide a quale "pixel" scatta un'operazione.

Ho contattato il venditore e mi hanno dato una versione corretta.

Errore di arrotondamento n. 3 Risolto.

5. Il riquadro "Riepilogo" in Windows Explorer accade proprio per troncare i valori quando si visualizza il DPI calcolato.

Accade che questo valore troncato rappresentasse lo stesso valore errato che lo strumento EMF-a-PDF utilizzava internamente. A parte questo, questa stranezza non contribuisce in modo significativo alla discussione.

Conclusioni

Poiché la mia domanda riguardava il futzing con DPI sui contesti del dispositivo, Mark è una buona risposta.

Si noti che corro con 120 dpi su WXP per tutto il tempo (caratteri grandi) che significa che metafilegraphics.dpix restituirà 120.

Il file EMF non sembra registrare ciò che il DPI era del contesto di riferimento (120 in questo caso, 96 per la maggior parte delle altre persone).

Per rendere le cose più interessanti, è possibile creare un EMF attraversando una bitmap in memoria che ha avuto setreSolution () impostato su, diciamo, 300 dpi. In tal caso, credo che il fattore di ridimensionamento debba essere 300 e non ciò che il monitor (86.x) o Windows (120) potrebbe utilizzare.

Sembra che i valori nella pagina di riepilogo siano sbagliati. Sono calcolati come:

Size = round(precize_size)+1
Resolution = trunc(precize_resolution)

Laddove i valori di pressione vengono calcolati senza arrotondamento o troncamento.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top