2012-06-28 31 views
24

Tôi đang cố gắng tạo danh sách phông chữ để người dùng chọn. Tôi đang làm điều này bằng cách sử dụng hàm EnumFontFamiliesEx nhưng không may, danh sách phông chữ được trả lại quá dài. Có rất nhiều phông chữ phụ có vẻ phù phiếm, trùng lặp, cho một ngôn ngữ khác hoặc không mong muốn hiển thị cho người dùng. Ảnh chụp màn hình của tôi minh họa tốt nhất phần rác mà tôi đang cố lọc ra.Quá nhiều phông chữ khi liệt kê bằng hàm EnumFontFamiliesEx

Mã của tôi để gọi EnumFontFamiliesEx trông như thế này:

LOGFONT lf; 
memset(&lf, 0, sizeof(lf)); 
lf.lfCharSet = DEFAULT_CHARSET; 
// screenDC is result of CreateCompatibleDC(NULL) 
EnumFontFamiliesEx(screenDC, &lf, GetFontsCallback, NULL, 0); 

Danh sách kết quả sẽ như thế này, sau khi phân loại theo thứ tự abc và loại bỏ phông chữ với tên mặt trùng lặp:

enter image description here

Như bạn có thể xem, hộp thoại phổ biến phông chữ ChooseFont hiển thị danh sách phông chữ rất hợp lý thân thiện với người dùng và có ý nghĩa. Mặt khác, mã của tôi hiển thị một danh sách dài các phông chữ bổ sung: phông chữ bắt đầu bằng "@" (tại sao? Chúng là gì?), 3 biến thể phông chữ Arial và một số phông chữ khác không rõ mục đích như Aheroni, Andalus, Angsana mới, AngsanaUPC, và như vậy. Thật điên rồ.

Làm cách nào để lọc danh sách phông chữ được trả về bởi EnumFontFamiliesEx sao cho nó chính xác khớp với danh sách được hiển thị trong hộp thoại ChooseFont?

+0

Các tài liệu là khá rõ ràng rằng một vài sự đối số sẽ thay đổi số/loại phông chữ được trả về. Bạn đang chuyển sang mặc định. Bạn đã cố gắng cụ thể hơn chưa? –

+0

Còn việc thêm tìm kiếm và bộ lọc trong giao diện người dùng của bạn thì sao? –

+0

Đây là thiên hướng văn hóa khá nặng. Người dùng Đông Á của bạn sẽ tìm thấy thứ đáng giá để chọn trong hộp thoại đó. Giống như tất cả những người có một @ ở phía trước của nó. Phông chữ hỗ trợ văn bản dọc. Bạn chắc chắn có thể khắc phục sự cố đó, gỡ cài đặt phông chữ mà bạn không quan tâm. –

Trả lời

25

Nhờ Jesse Good, bây giờ tôi đã tìm hiểu về một số quyết định thiết kế không may đáng sợ được thực hiện bởi nhóm Windows 7. Tôi sẽ không chấp nhận câu trả lời của riêng tôi, bởi vì nếu ai đó đưa ra cách sử dụng tính năng phông chữ ẩn này trong Windows 7 ngay cả khi khoá đăng ký không tồn tại (ví dụ: có thể bằng cách sử dụng API không có giấy tờ hoặc một số khác trickery) và câu trả lời của họ hoạt động, tôi sẽ chấp nhận nó.

Bộ lọc này được thực hiện bằng các phông chữ "ẩn" thực sự trong Bảng điều khiển của Windows 7. Theo mặc định, phông chữ cho các ngôn ngữ khác bị ẩn nhưng chúng có thể được hiển thị bởi người dùng. Ít nhất, đây là ý tưởng. Đây là trang MSDN thảo luận về tính năng này: International Font Management.

Dưới đây là một số trích đoạn quan trọng từ trang này và các trang khác gần đó trong MSDN (xem thêm http://msdn.microsoft.com/en-us/library/windows/desktop/dd371704(v=vs.85).aspx từ Windows 7 tương thích sách dạy nấu ăn):

Bắt đầu với Windows 7, cơ sở hạ tầng quản lý font chữ hỗ trợ ẩn của phông chữ không phù hợp với danh sách lựa chọn phông chữ của người dùng. ... Tính năng này có nghĩa là người dùng không còn phải đối mặt với danh sách dài các phông chữ không phù hợp nữa.

Trong Windows 7, không có API để truy vấn trực tiếp phông chữ nào bị ẩn hoặc để đặt phông chữ bị ẩn. [nhấn mạnh mỏ] Nếu bạn sử dụng Windows ChooseFont API (Font common dialog) để kích hoạt lựa chọn phông chữ ngày hôm nay, bạn sẽ nhận được hành vi mới miễn phí. Windows Scenic Ribbon (Font Controls) mới được giới thiệu trong Windows 7 cũng hỗ trợ hành vi này và cung cấp một lý do khác để "Ribbonize" các ứng dụng của bạn.

Khi phông chữ được chọn vào ngữ cảnh thiết bị, sẽ không có ảnh hưởng đến bản vẽ do phông chữ bị ẩn. Chức năng EnumFontFamiliesEx tiếp tục liệt kê các phông chữ được đặt thành ẩn. [nhấn mạnh mỏ; dường như không có cách nào để phân biệt các phông chữ ẩn và hiển thị với EnumFontFamiliesEx]

Lưu ý rằng bộ ký tự là một khái niệm cũ tương ứng với bộ ký tự tiền Unicode. [nhấn mạnh mỏ]

ChọnFont sẽ chỉ liệt kê các phông chữ được hiển thị và lọc ra các phông chữ ẩn trong khi hiển thị phông chữ trong hộp danh sách. Cờ bổ sung (CF_INACTIVEFONTS) trong cờ thành viên của cấu trúc CHOOSEFONT được thêm vào để cho phép bạn hiển thị tất cả các phông chữ được cài đặt trong danh sách phông chữ, giống như ChooseFont cư xử trước Windows 7.

Nói cách khác, trừ khi bạn sử dụng hộp thoại chung ChooseFont hoặc điều khiển ribbon chính thức của Windows (chỉ có sẵn trên Windows Vista/7), bạn không có cách nào được hỗ trợ ở tất cả các phông chữ bị ẩn. Có bất ngờ hay ngạc nhiên khi nhiều người dùng trên Internet đang phàn nàn rằng việc ẩn các phông chữ trong Bảng điều khiển Windows 7 dường như không có hiệu lực?!? (Trước đây tôi đã giả mạo rằng MS Word 2010 lọc ra các phông chữ bị ẩn. Có vẻ như nó không có, bởi vì họ sử dụng điều khiển ribbon tùy chỉnh của riêng mình chứ không phải ruy-băng được tích hợp trong Windows. không tương thích với một trong những sản phẩm hàng đầu của Microsoft và không thể tương thích mà không phải bán ruy băng mạnh mẽ hơn trong Office.)

Dựa trên liên kết mà Jesse Good đăng, tôi biết rằng các phông chữ ẩn được lưu trữ trong một đăng ký không có giấy tờ Chìa khóa. Thông qua liên kết này, và cũng có một số thử nghiệm và phân tích với Process Monitor (nhìn vào cả đống dấu vết và đăng ký truy cập), tôi đã học như sau:

  • Việc kiểm soát băng gọi một chức năng không có giấy tờ gọi FmsGetFilteredFontList trong FMS.DLL (Font Dịch vụ quản lý). Mục đích của nó xuất hiện khá rõ ràng. Đó là một sự xấu hổ thực sự họ không thể bị làm phiền để công khai tài liệu và duy trì nó.
  • Cài đặt được lưu trữ trong khóa đăng ký không có giấy tờ, được truy cập bởi FMS.DLL.
  • Nếu khóa đăng ký bị xóa, nó được tạo lại với cài đặt mặc định bằng FmsGetFilteredFontList, để ẩn các phông chữ không liên quan đến ngôn ngữ nhập hiện tại.
  • Một hồ sơ người dùng hoàn toàn mới được tạo trên cài đặt Windows sạch sẽ KHÔNG chứa bất kỳ khóa đăng ký nào liên quan đến phông chữ nào sẽ bị ẩn.

Do đó, liên kết được đăng bởi Jesse Good có thể làm việc cho nhiều/hầu hết các trường hợp, nhưng không phải 100% thời gian. Bạn cần một cách để tái tạo một cách đáng tin cậy các khóa registry này (hoặc ít nhất là giả định mặc định) nếu chúng không tồn tại. Hành vi mặc định vẫn là ẩn một số phông chữ, ngay cả khi các khóa đăng ký đã biến mất (ví dụ: trên hồ sơ người dùng mới).

+4

+1 cho nghiên cứu. Thật là một mớ hỗn độn vô lý. Microsoft đã nhận được rất tốt trong việc triển khai 50% các giải pháp gần đây. –

4

Do FmsGetFilteredFontList không có giấy tờ, các tùy chọn để nhận chính xác cùng danh sách mà người dùng nhìn thấy trong hộp thoại Windows 7+ ChooseFont có thể bị giới hạn. Tuy nhiên, nó có thể có được một xấp xỉ tốt với danh sách phông chữ mặc định bằng cách chỉ sử dụng các API được ghi lại.

Tôi đã thực hiện điều gì đó tương tự để giảm số lượng khả năng cho thuật toán tự động chọn phông chữ phù hợp.

Cách tiếp cận của tôi là sử dụng bitmap bit phụ Unicode trong FONTSIGNATURE, có thể được kiểm tra khi bạn liệt kê phông chữ. Nếu bạn biết (các) thứ hạng Unicode nào bạn cần, chữ ký phông chữ sẽ cho bạn biết phông chữ hiện có có bao gồm không. Nếu có, hãy đưa nó vào danh sách. Nếu không, hãy bỏ qua.Tôi nghi ngờ điều này có thể tương tự như cách FmsGetFilteredFontList xây dựng danh sách mặc định của nó.

Bí quyết là tìm ra (những) thứ hạng mà người dùng cần. Trong trường hợp của tôi, nó là tương đối dễ dàng, bởi vì tôi biết chính xác những gì văn bản tôi sẽ phải render. Tôi đã xây dựng một ánh xạ các subrang thành các giá trị bitmap theo FONTSIGNATURE dựa trên documentation.

Tôi đã quét các điểm mã trong văn bản được hiển thị, tìm kiếm chúng trong ánh xạ và xây dựng bitmap mục tiêu. Tôi bitwise và bitmask mục tiêu này với một trong chữ ký phông chữ cho mỗi phông chữ liệt kê. Bất cứ khi nào kết quả phù hợp với bitmap mục tiêu, tôi biết phông chữ có thể (rất có thể) hỗ trợ văn bản. Đối với đơn đăng ký của tôi, tôi yêu cầu tất cả các bit mục tiêu phải có trong phông chữ. Đối với ứng dụng của bạn, tôi nghĩ bạn muốn bất kỳ nào của các bit mục tiêu.

Bitmask chữ ký phông chữ là lần cắt đầu tiên vào những ký tự mà phông chữ cung cấp. Bạn có thể sử dụng GetFontUnicodeRanges để hoàn toàn chắc chắn, nhưng tôi thấy rằng không cần thiết và nó cũng chậm hơn so với chỉ kiểm tra chữ ký phông chữ.

Trong trường hợp của bạn, có lẽ bạn muốn có một số chuỗi văn bản đại diện có sẵn bằng ngôn ngữ của người dùng. Ví dụ: từ tài liệu họ đang chỉnh sửa hoặc từ tài nguyên giao diện người dùng đã được dịch. Bạn có thể quét văn bản mẫu đó để lấy chữ ký phông đích.

Ví dụ: nếu bạn quét một số văn bản tiếng Anh, bạn sẽ thấy rằng tất cả các ký tự cần thiết đều nằm trong phân vị Latin. Nếu bạn nhìn vào applet bảng điều khiển phông chữ trong Windows 7 cho người dùng tiếng Anh (và chuyển sang chế độ xem chi tiết), bạn sẽ thấy cột Hiện/ẩn tương quan chặt chẽ với liệu tiếng Latin có được liệt kê trong Được thiết kế cho cột, có vẻ như là một biểu diễn văn bản của bitmap mặt nạ Unicode của chữ ký phông chữ.

Cập nhật: Tôi vừa thử liệt kê phông chữ bằng DirectWrite, nghĩ rằng API mới hơn này có thể xử lý tính năng ẩn phông chữ. Than ôi, nó trả về tất cả mọi thứ và không có tùy chọn (mà tôi có thể tìm thấy) để lọc ra các phông chữ ẩn.

1

Thật đáng hổ thẹn khi Microsoft không ghi lại chức năng này, thành thật mà nói, nhưng ngày càng nhiều, đây là những gì chúng tôi mong đợi từ họ.

Một cách khác để lọc danh sách phông chữ của riêng bạn là tận dụng vỏ bằng cách liệt kê thư mục phông chữ. Nếu bạn nhìn bằng Explorer, bạn sẽ thấy các phông chữ ẩn được hiển thị với một biểu tượng bị mờ - chúng ta có thể sử dụng thuộc tính đó để biết phông chữ có bị ẩn hay không.

Ví dụ (không hoàn thành):

LPITEMIDLIST pidlFonts; 
if (SUCCEEDED(SHGetKnownFolderIDList(FOLDERID_Fonts, 0, nullptr, &pidlFonts))) 
{ 
    CComPtr<IShellFolder> psf; 
    if (SUCCEEDED(SHBindToObject(nullptr, pidlFonts, nullptr, IID_IShellFolder, reinterpret_cast<void**>(&psf)))) 
    { 
     CComPtr<IEnumIDList> pEnum; 
     if (SUCCEEDED(psf->EnumObjects(hWnd, SHCONTF_FOLDERS | SHCONTF_NONFOLDERS | SHCONTF_INCLUDEHIDDEN | SHCONTF_INIT_ON_FIRST_NEXT, &pEnum))) 
     { 
      LPITEMIDLIST pidl; 
      ULONG celt = 0; 
      while (pEnum->Next(1, &pidl, &celt) == S_OK) 
      { 
       SFGAOF hidden = SFGAO_GHOSTED; 
       if (SUCCEEDED(psf->GetAttributesOf(1, const_cast<LPCITEMIDLIST*>(&pidl), &hidden)) && (hidden & SFGAO_GHOSTED) == SFGAO_GHOSTED) 
       { 
        // this font should be hidden 
        // get its name via IShellFolder::GetDisplayNameOf 
       } 
       CoTaskMemFree(pidl); 
      } 
     } 
    } 
    CoTaskMemFree(pidlFonts); 
} 

Bạn có thể sử dụng phương pháp này để xây dựng một tập hợp các phông chữ ẩn và sau đó sử dụng để lọc các kết quả của EnumFontFamiliesEx.

+1

Đây chắc chắn không phải là cách chính xác để làm điều đó. – Elmue

+3

Ý của bạn là gì? Không có cách "chính xác" để làm điều đó vì API không được ghi lại. –

-3

Tôi nghĩ rằng toàn bộ cuộc thảo luận ở đây là gây hiểu lầm.

Khi tôi cung cấp bộ chọn phông chữ cho người dùng của mình, tại sao tôi nên quan tâm những phông chữ nào bị Microsoft ẩn? Và tại sao tôi nên ẩn tất cả các phông chữ mà Microsoft cho rằng nên ẩn theo mặc định?

Điều gì sẽ xảy ra nếu người dùng của tôi muốn sử dụng một trong những phông chữ mà Microsoft đã ẩn? Tôi có nên đặt gánh nặng lên người dùng của mình để đi tới bảng điều khiển để bỏ ẩn phông chữ này không?

Điều gì sẽ xảy ra nếu một ngày nào đó người dùng Trung Quốc muốn viết văn bản tiếng Trung trên Windows tiếng Anh và phông chữ tiếng Trung bị ẩn?

Tôi nghĩ có một cách tốt hơn để hạn chế số lượng phông chữ lớn được trả về bởi EnumFontFamiliesEx().

Tôi đã viết bộ chọn phông chữ của riêng mình có bộ lọc phông chữ cho phép người dùng chọn nhóm phông chữ mà anh ấy muốn sử dụng. Bằng cách này tôi không che giấu bất cứ điều gì và cung cấp cho tất cả các quyền lực cho người dùng hơn là để Microsoft!

Người dùng có thể MUỐN xem TẤT CẢ phông chữ! Đôi khi một người chỉ cần Arial Black hoặc Arial Narrow mặc dù Microsoft cho rằng nó sẽ bị ẩn.

int CALLBACK EnumFontFamExProc(const LOGFONT* pk_Font, 
           const TEXTMETRIC* pk_Metric, 
           DWORD e_FontType, 
           LPARAM lParam) 
{ 
    if (e_FontType & TRUETYPE_FONTTYPE) 
    { 
     // u32_Flags128 = DWORD[4] = 4 * 32 bit = 128 bit 
     DWORD* u32_Flags128 = ((NEWTEXTMETRICEX*)pk_Metric)->ntmFontSig.fsUsb; 

     if (u32_Flags128[13/32] & (1 << (13 % 32))) 
     { 
      // the font contains arabic characters (bit 13) 
     } 
     if (u32_Flags128[38/32] & (1 << (38 % 32))) 
     { 
      // the font contains mathematical symbols (bit 38) 
     } 
     if (u32_Flags128[70/32] & (1 << (70 % 32))) 
     { 
      // the font contains tibetan characters (bit 70) 
     } 
    } 

Trong khi gọi lại, bạn nhận được cờ 128 bit xác định chính xác vùng Unicode nào được phông chữ hỗ trợ.

Xem http://msdn.microsoft.com/en-us/library/dd374090%28v=vs.85%29.aspx

Bạn có thể sử dụng những 128 Bits để lọc và làm giảm số lượng các phông chữ mà bạn hiển thị trong danh sách phông chữ:

enter image description here

+4

Bạn đã bỏ lỡ điểm. Các phông chữ không bị "ẩn bởi Microsoft". Chúng bị ẩn bởi người dùng. Microsoft ẩn một số theo mặc định, nhưng người dùng có quyền kiểm soát cuối cùng. –

+0

Không. Tôi hiểu hoàn toàn. Tôi không muốn đặt gánh nặng cho người dùng của mình rằng họ phải vào bảng điều khiển để kích hoạt Arial Black bị ẩn bởi các mặc định của Microsoft. Tôi muốn cho phép người dùng chọn bất kỳ phông chữ nào mà anh ta đã cài đặt mà không làm cho nó phức tạp hơn mức cần thiết. – Elmue

+0

Vậy mã của bạn làm gì? ... – Lazik

Các vấn đề liên quan