Używam komunikatu do renderowania danych wyjściowych formantu tekstu formatowanego do dowolnego kontekstu urządzenia. Jednak podczas renderowania do bitmapy, kropki na cal w kontekście urządzenia bitmapy są takie same jak w przypadku DPI urządzenia wyświetlającego, czyli 96 punktów na cal. Jest to znacznie mniej niż to, na co chciałbym oddać. Wolałbym renderować przy znacznie wyższej wartości DPI, aby użytkownik mógł powiększać i być może później drukować na drukarce o wysokiej czułości.Błędy zaokrąglania podczas skalowania renderowanego wyjścia formantu edycji rozszerzonej za pomocą EM_FORMATRANGE
Podejrzewam, że dzieje się tak, że sterowanie RTF wywołuje GetDeviceCaps
z LOGPIXELSX
i LOGPIXELSY
, aby uzyskać liczbę pikseli na cal urządzenia. Następnie renderuje dokument przy użyciu tej wartości DPI przy poziomie powiększenia 100%. Urządzenia wyświetlające Windows zawsze zwracają wartość 96 DPI, o ile w systemie nie są używane duże czcionki (ustawione w Panelu sterowania), a aplikacja obsługuje DPI.
Wiele przykładów w Internecie proponuje skalowanie wyjścia EM_FORMATRANGE
. Jest tak, aby można było uzyskać dowolną rozdzielczość DPI. Większość przykładów na ogół obejmuje używanie SetMapMode
, i SetViewportExtEx
(np. Patrz http://social.msdn.microsoft.com/Forums/en-us/netfxbcl/thread/37fd1bfb-f07b-421d-9b5e-5f4492ffbbc3). Za pomocą tych funkcji można skalować wyniki renderowania formantu tekstu w formacie RTF: na przykład, jeśli określę skalowanie 400%, to jeśli formant tekstu sformatowanego wykona obraz o szerokości 5 pikseli, będzie on miał szerokość 20 pikseli.
Niestety, stare funkcje GDI używają liczb całkowitych zamiast liczb zmiennoprzecinkowych. Załóżmy na przykład, że kontrola RTF zdecydowała, że element powinien zostać narysowany w (12,7, 15,3) pikselach. Zostałoby to zaokrąglone do pozycji (13, 15). Te zaokrąglone współrzędne są przekazywane do GDI, które następnie skalują obraz za pomocą skalowania określonego przez SetMapMode
: na przykład 400%, będzie to (13 * 4, 15 * 4) lub (52, 60). Ale nie jest to dokładne: element lepiej byłoby umieścić w (12,7 * 4, 15,3 * 4) lub (51, 61). Najgorsze jest to, że w niektórych przypadkach błąd staje się kumulatywny.
Wierzę, że to jest przyczyna tego bardzo zauważalny błąd podczas skalowania jakiś prosty tekst:
Powyższy przykład jest 8 punkt Segoe UI, skalowane do 400% przy użyciu EM_FORMATRANGE
i SetMapMode
na zasadzie Kontekst urządzenia wyświetlającego 96 DPI. Tekst ma teraz rozmiar 32-punktowy, ale przestrzeń między poszczególnymi postaciami jest zbyt wysoka i wygląda nienaturalnie.
Powyższy przykład został utworzony w WordPadzie wprowadzając tekst o 8 punktów Segoe interfejsu a następnie za pomocą regulatora powiększenia ustawiona na 400% stopniu powiększenia. Przestrzeń między każdą postacią wygląda normalnie. Dokładnie taki sam wynik uzyskuje się za pomocą czcionki 32-punktowej i 100-procentowego zoomu.
Aby obejść ten problem, próbowałem wykonać następujące czynności. Dla każdego wypróbowanego wyniku wynik był identycznie niezadowalający, gdy skalowany do 400%.
- Korzystanie z transformacji skalowania ustawić za pomocą
SetWorldTransform
zamiast skalowania wykonanej zSetMapMode
iSetWindowExtEx
itp - Przechodząc kontekstu urządzenia dla metafile do
EM_FORMATRANGE
, a następnie skalowanie metapliku później. - Skalowanie w połączeniu z renderowaniem do metapliku, a następnie wyświetlenie metapliku później bez skalowania.
wierzę, wyniki zawsze są niezadowalające, ponieważ problem sprowadza się do tego, że bogaty kontrola edycji jest zaokrąglenie do najbliższej liczby całkowitej i renderowania do tego, co myśli, to urządzenie 96 DPI - ignorując transformacji w miejscu. Zajrzałem do formatu metapliku i odkryłem, że poszczególne pozycje postaci są przechowywane w metapliku w rozdzielczości pikseli - dlatego skalowanie metapliku oczywiście nie działało, ponieważ zaokrąglenie już nastąpiło w tym punkcie.
mogę myśleć o dwóch realnych rozwiązań, które obejść ten problem:
- Użyj kontekst urządzenia z wyższych punktów określonych przez użytkownika na cal, tak że
GetDeviceCaps
zwroty różnych wartościach. (Uwaga: niektóre przykłady proponują używanie drukarki, ponieważ generalnie mają wyższą wartość DPI, ale chcę, aby mój kod działał na systemach, które nie mają drukarki i mogą być renderowane do bufora poza ekranem). - W jakiś sposób można powiedzieć, że formant edycji rozszerzonej przyjmuje, że kontekst urządzenia ma inną kropkę na cal niż raportowany przez
GetDeviceCaps
.
Wszystko inne wydaje się być nadal przedmiotem tych błędów zaokrąglania.
Czy ktokolwiek (1) ma pomysł, jak wdrożyć którekolwiek z proponowanych przeze mnie rozwiązań, lub (2) mam alternatywny pomysł, jak osiągnąć cel, jakim jest uzyskanie dokładnego wyjścia o wysokiej rozdzielczości w buforze?
Skalowanie mapy bitowej to handel jednym złem za drugiego. Wynikowy obraz byłby niewyraźny: po co zawracać sobie głowę wyjściami do obrazu o wysokiej rozdzielczości? Podejście metapliku również nie zadziałało - sądzę, że z powodu tych samych problemów z błędem zaokrąglania - wyniki były identycznie błędne. –
Pomyślałem tylko o dwóch możliwych rozwiązaniach: (1) Stwórz bogatą w oknach edycję bogatą w edycję, powiększ ją i poproś o renderowanie do dowolnego kontekstu urządzenia, który w rzeczywistości nie jest oknem. To może być najlepszy sposób, ponieważ używa obsługiwanego interfejsu. (2) przechwyć funkcję GetDeviceCaps i spraw, by kłamała ona o LOGPIXELSX i LOGPIXELSY dla konkretnego kontekstu urządzenia, aby oszukać formant edycji edycyjnej do renderowania z pożądanym DPI. Ale oczywiście nie będzie to wspierane/nieudokumentowane. Nie miałem czasu zbadać żadnego z tych rozwiązań. Jeśli otrzymasz działające rozwiązanie, chciałbym o tym usłyszeć! –
rozwiązanie (1): z mojego doświadczenia, próbując narysować tekst gdi (nie gdi +) na powierzchni, która nie jest powierzchnią ekranu, da ci złe wyniki, ponieważ wygładzanie czcionek nie zadziała (przez te dni zmagałem się z tym problemem). rozwiązanie (2): nie mam pojęcia, jak to zrobić :) Czy masz XP w tym polu? – Roey