Question

J'ai une application qui génère des métafiles (EMF). Il utilise le dispositif de référence (aka l'écran) pour rendre ces métafiles, de sorte que le DPI du métafile change en fonction de la machine sur laquelle le code fonctionne.

Disons que mon code a l'intention de créer un métafile de 8,5 en x 11 po. En utilisant mon poste de travail de développement comme référence, je me retrouve avec un FEM qui a

  • un rclframe de {0, 0, 21590, 27940} (dimensions du métafile, en millièmes de mm)
  • Un szldevice de {1440, 900} (dimensions du dispositif de référence, en pixels)
  • un szlmillimeters de {416, 260} (dimensions du dispositif de référence, en mm)

D'accord, donc le rclframe me dit que la taille de l'EMF devrait être

  • 21590/2540 = 8,5 en large
  • 27940/2540 = 11 en hauteur

Directement sur. En utilisant ces informations, nous pouvons également déterminer le DPI physique de mon moniteur, si mes mathématiques sont correctes:

  • (1440 * 25,4) / 416 = 87,9231 DPI horizontal
  • (900 * 25,4) / 260 = 87,9231 DPI vertical

Le problème

Tout ce qui revient à ce métafile - une conversion EMF-To-PDF, la page "Résumé" lorsque vous faites un clic droit sur l'EMF dans Windows Explorer, etc. - Seem pour tronquer la valeur DPI calculée, affichant 87 au lieu de 87.9231 (même 88 serait bien).

Il en résulte une page de taille physique comme 8,48 en x 10,98 dans (en utilisant 87 dpi) au lieu de 8,5 dans x 11 po (en utilisant 88 dpi) lorsque le métafile est joué en arrière.

  • Est-il possible de modifier le DPI du dispositif de référence afin que les informations stockées dans le métafile utilisées pour calculer le DPI s'approche d'un joli entier?
  • Puis-je créer mon propre contexte d'appareil et spécifier son DPI? Ou dois-je vraiment utiliser une imprimante pour le faire?

Merci pour tout aperçu.

Était-ce utile?

La solution

Je suis curieux de savoir comment Windows connaît la taille physique de votre moniteur. Vous devez avoir changé une configuration quelque part? Vous pouvez peut-être le changer en valeurs plus pratiques qui se divisent bien.

Comme l'indique le nom, un «contexte de périphérique» doit être connecté à un périphérique système. Cependant, cela n'a pas besoin d'être un pilote matériel, il pourrait s'agir d'un émulateur d'appareil tel qu'un pilote d'impression PDF. J'en ai vu au moins un qui vous permet de définir un DPI arbitraire.

Autres conseils

J'ai maintenant appris plus que je ne le souciais des métafiles.

1. Une partie de la Metafile Les surcharges de constructeur de la classe fonctionnent mal et fonctionneront sur une valeur DPI tronquée.

Considérer ce qui suit:

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;
}

Si vous êtes passé dans un SizeF de {8.5, 11}, vous pourriez vous attendre à obtenir un Metafile qui a un rclFrame de {21590, 27940}. La conversion des pouces en millimètres n'est pas difficile, après tout. Mais vous ne le ferez probablement pas. Selon votre résolution, GDI +, semble-t-il, utilisera une valeur DPI tronquée lors de la conversion du paramètre des pouces. Pour bien faire les choses, je dois le faire moi-même dans des centièmes de millimètre, que GDI + passe juste car c'est comme ça qu'il est stocké nativement dans l'en-tête métafile:

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

Erreur d'arrondi n ° 1 résolu - le rclFrame de mon métafile est maintenant correct.

2. Le DPI sur un Graphics Enregistrement d'instance à un Metafile est toujours faux.

Regarde ça metafileGraphics variable que j'ai définie en appelant Graphics.FromImage() sur le métafile? Eh bien, il semble que Graphics L'instance aura toujours un DPI de 96 dpi. (Si je devais deviner, il est toujours réglé sur le logique DPI, pas le physique une.)

Vous pouvez imaginer cette hilarité qui s'ensuit lorsque vous dessinez sur un Graphics instance qui fonctionne sous 96 dpi et enregistre sur un Metafile Instance qui a 87,9231 DPI "enregistré" dans son en-tête. (Je dis "enregistré" parce qu'il est calculé à partir des autres valeurs.) Les "pixels" du métafile (rappelez-vous, les commandes GDI stockées dans le métafile sont spécifiées dans pixels) sont plus gros, et donc vous maudissez et murmurez pourquoi votre appel à dessiner quelque chose d'un pouce se termine par un et quelque chose de plus de quelques pouces.

La solution consiste à réduire le Graphics exemple:


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

N'est-ce pas une huée? Mais cela semble fonctionner.

L'erreur "d'arrondissement" # 2 résolu - quand je dis dessiner quelque chose à "1 pouce" à 88 dpi, ce pixel ferait mieux de $% $ ^! Enregistré comme pixel # 88.

3. szlMillimeters peut varier sauvagement; Remote Desktop provoque beaucoup de plaisir.

Donc, nous avons découvert (par réponse de Mark) que, parfois, Windows interroge l'éditte de votre moniteur et sait réellement à quel point il est physique. GDI + utilise utilement ceci (HORZSIZE etc) lors du remplissage du szlMillimeters propriété.

Imaginez maintenant que vous rentrez chez vous pour déboguer ce code de bureau distant. Disons que votre ordinateur domestique a un moniteur largeur de 16: 9.

De toute évidence, Windows ne peut pas interroger l'éditte d'un écran distant. Il utilise donc la valeur par défaut séculaire de 320 x 240 mm, ce qui serait bien, sauf qu'il se trouve que c'est un rapport d'aspect 4: 3, et maintenant le même code exactement génère un métafile sur un affichage qui n'a pas non Pixels physiques carrés: le DPI horizontal et le DPI vertical sont différents, et je ne me souviens pas de la dernière fois que j'ai vu cela se produire.

Ma solution de contournement pour l'instant est: "Eh bien, ne l'exécutez pas sous le bureau distant."

4. L'outil EMF-TO-PDF que j'utilisais avait une erreur d'arrondi en regardant le rclFrame entête.

C'est la principale cause de mon problème qui a déclenché cette question. Mon métafile était "correct" depuis le début (enfin, correct après avoir résolu les deux premiers numéros), et toute cette recherche pour créer un métafile "haute résolution" était un hareng rouge. Il est vrai qu'une certaine fidélité est perdue lors de l'enregistrement du métafile sur un dispositif d'affichage à basse résolution; En effet, les commandes GDI spécifiées dans le métafile sont spécifiées dans les pixels. Peu importe qu'il s'agisse d'un format vectoriel et peut évoluer ou vers le bas, certaines informations sont perdues Pendant l'enregistrement réel Lorsque GDI + décide quel "pixel" pour casser une opération.

J'ai contacté le vendeur et ils m'ont donné une version corrigée.

Erreur d'arrondi # 3 résolu.

5. Le volet «Résumé» dans Windows Explorer se produit juste pour tronquer les valeurs lors de l'affichage du DPI calculé.

Il se trouve que cette valeur tronquée représentait la même valeur erronée que l'outil EMF-PDF utilisait en interne. En plus de cela, cette bizarrerie ne contribue rien de significative à la discussion.

Conclusions

Étant donné que ma question concernait le fuir avec DPI sur les contextes de l'appareil, Mark's est une bonne réponse.

Notez que j'exécute avec 120 dpi sur WXP tout le temps (grandes polices), ce qui signifie que MetafileGraphics.dpix reviendra 120.

Le fichier EMF ne semble pas enregistrer ce que le DPI était du contexte de référence (120 dans ce cas, 96 pour la plupart des autres personnes).

Pour rendre les choses plus intéressantes, il est possible de créer un EMF via le dessin sur un bitmap en mémoire qui a été défini par SetResolution () pour, disons, 300dpi. Dans ce cas, je crois que le facteur de mise à l'échelle doit être 300 et non ce que le moniteur (86.x) ou Windows (120) pourrait utiliser.

On dirait que les valeurs sur la page de résumé sont erronées. Ils sont calculés comme:

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

Où les valeurs précises sont calculées sans arrondissement ni troncature.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top