2015-12-14 5 views
13

मैं कुछ .wmf फ़ाइलों को बना रहा हूं, लेकिन उनमें से कुछ दूषित लगते हैं और किसी भी मेटाफाइल दर्शक में नहीं दिखाए जा सकते हैं। कुछ परीक्षण और त्रुटि के बाद, मैंने पाया कि समस्या उनके आयामों के कारण होती है। यदि मैं आयाम को कम करने के लिए एक कारक द्वारा एक ही ड्राइंग को स्केल करता हूं, तो यह दिखाया जाएगा।क्या विंडोज मेटाफाइल के आयामों पर कोई सीमा है?

अब, मैं जानना चाहता हूं कि ड्राइंग के आकार पर कोई सीमा है या यदि समस्या कुछ और है। मुझे पता है कि ये फ़ाइलें have a 16-bit data structure है, इसलिए मुझे लगता है कि सीमा प्रत्येक आयाम में 2^16 इकाइयां होगी, (या 2^15 अगर यह हस्ताक्षरित है)। लेकिन मेरे परीक्षणों में यह लगभग 25,000 है। तो मैं इस मूल्य पर भरोसा नहीं कर सकता क्योंकि सीमा कुछ भी हो सकती है (चौड़ाई * ऊंचाई शायद, या शायद चित्र का संकल्प इसे प्रभावित कर सकता है)। मुझे .wmf फ़ाइलों के बारे में एक विश्वसनीय संसाधन नहीं मिल रहा है जो इसका वर्णन करता है।

यहाँ नमूना कोड है कि समस्या से पता चलता है:

procedure DrawWMF(const Rect: TRect; const Scale: Double; FileName: string); 
var 
    Metafile: TMetafile; 
    Canvas: TMetafileCanvas; 
    W, H: Integer; 
begin 
    W := Round(Rect.Width * Scale); 
    H := Round(Rect.Height * Scale); 

    Metafile := TMetafile.Create; 
    Metafile.SetSize(W, H); 

    Canvas := TMetafileCanvas.Create(Metafile, 0); 
    Canvas.LineTo(W, H); 
    Canvas.Free; 

    Metafile.SaveToFile(FileName); 
    Metafile.Free; 
end; 

procedure TForm1.Button1Click(Sender: TObject); 
const 
    Dim = 40000; 
begin 
    DrawWMF(Rect(0, 0, Dim, Dim), 1.0, 'Original.wmf'); 
    DrawWMF(Rect(0, 0, Dim, Dim), 0.5, 'Scaled.wmf'); 

    try 
    Image1.Picture.LoadFromFile('Original.wmf'); 
    except 
    Image1.Picture.Assign(nil); 
    end; 

    try 
    Image2.Picture.LoadFromFile('Scaled.wmf'); 
    except 
    Image2.Picture.Assign(nil); 
    end; 
end; 

पुनश्च: मुझे पता है कि True को Metafile.Enhanced की स्थापना और एक .emf फ़ाइल के रूप में सहेजने समस्या का समाधान होगा, लेकिन गंतव्य अनुप्रयोग है कि मैं मैं फ़ाइलों को उत्पन्न करने के लिए एन्हांस्ड मेटाफाइल का समर्थन नहीं करता हूं।

संपादित करें: नीचे जवाब में उल्लेख किया है, वहाँ दो अलग अलग यहां समस्याएं हैं:

मुख्य समस्या, फ़ाइल स्वयं के बारे में है यह प्रत्येक आयाम पर एक 2^15 सीमित है। यदि ड्राइंग की चौड़ाई या ऊंचाई इस मान को पार करती है, तो डेल्फी दूषित फ़ाइल लिखेंगे। आप Sertac's answer में अधिक जानकारी प्राप्त कर सकते हैं।

दूसरी समस्या फ़ाइल को TImage में लोड करने के बारे में है। जब आप डेल्फी वीसीएल एप्लिकेशन में छवि दिखाना चाहते हैं तो एक और सीमा है। यह एक प्रणाली निर्भर है और डीसी के डीपीआई से संबंधित है कि चित्रण को चित्रित किया जा रहा है। Tom's answer विवरण में इसका वर्णन करता है। 0.7 के रूप में ScaleDrawWMF (ऊपर कोड नमूना) इस पीसी को मेरे पीसी पर पुन: उत्पन्न करता है। जेनरेट की गई फ़ाइल ठीक है और अन्य मेटाफाइल दर्शक (मैं एमएस ऑफिस पिक्चर मैनेजर का उपयोग करता हूं) के साथ देखा जा सकता है लेकिन वीसीएल इसे दिखाने में विफल रहता है, हालांकि, फाइल लोड करते समय कोई अपवाद नहीं उठाया जाता है।

+1

मैं WinAPI के रूप में फिर से टैग किए गए के बाद से इस है, मैं नहीं बल्कि विश्वास है, WMF प्रारूप बारे में एक प्रश्न से संबंधित कुछ भी की तुलना में: आप एक साधारण मानसिक स्वास्थ्य की जांच के बाद आप छवियों लोड किया है उन्हें ठीक से प्रदर्शित करने के लिए के लिए प्रदर्शन कर सकते हैं डेल्फी। –

+1

क्या आप सुनिश्चित हैं कि आपकी सीमा लगभग 25000 है? क्या यह शायद, वास्तव में, 32767 हो सकता है? –

+1

यह प्रतीत होता है कि यह कुल क्षेत्र (चौड़ाई × ऊंचाई) के आधार पर स्मृति आवंटन विफलता है। –

उत्तर

8

आपकी सीमा 32767.

VCL कोड ट्रेसिंग, आउटपुट फ़ाइल TMetafile.WriteWMFStream में भ्रष्ट हो जाता है है।वीसीएल वीसीएल में WmfPlaceableFileHeader (TMetafileHeader) रिकॉर्ड लिखता है और फिर 'emf' रिकॉर्ड्स को 'wmf' रिकॉर्ड्स में परिवर्तित करने के लिए GetWinMetaFileBits पर कॉल करता है। यह फ़ंक्शन विफल रहता है यदि बाउंडिंग आयत के किसी भी आयाम (CreateEnhMetaFile पर कॉल करते समय उपयोग किया जाता है) 32767 से अधिक है। वापसी मूल्य की जांच नहीं कर रहा है, वीसीएल कोई अपवाद नहीं उठाता है और फ़ाइल को केवल 22 बाइट्स से बंद करता है - केवल "प्लेटेबल हेडर" "।

आयाम 32767 की तुलना में कम, "placeable हैडर" संभव गलत मूल्यों (कारण के बारे में और जवाब देने के लिए टिप्पणियों Tom's answer से निहितार्थ विवरण और पढ़ें) हो सकता है यहां तक ​​कि के लिए, लेकिन यह बाद में अधिक ...

मैंने सीमा को खोजने के लिए नीचे दिए गए कोड का उपयोग किया। ध्यान दें कि GetWinMetaFileBits को वीसीएल कोड में एक उन्नत मेटाफाइल के साथ कॉल नहीं किया जाता है।

function IsDimOverLimit(W, H: Integer): Boolean; 
var 
    Metafile: TMetafile; 
    RefDC: HDC; 
begin 
    Metafile := TMetafile.Create; 
    Metafile.SetSize(W, H); 
    RefDC := GetDC(0); 
    TMetafileCanvas.Create(Metafile, RefDC).Free; 
    Result := GetWinMetaFileBits(MetaFile.Handle, 0, nil, MM_ANISOTROPIC, RefDC) > 0; 
    ReleaseDC(0, RefDC); 
    Metafile.Free; 
end; 

procedure TForm1.Button1Click(Sender: TObject); 
var 
    i: Integer; 
begin 
    for i := 20000 to 40000 do 
    if not IsDimOverLimit(100, i) then begin 
     ShowMessage(SysErrorMessage(GetLastError)); // ReleaseDc and freeing meta file does not set any last error 
     Break; 
    end; 
end; 

त्रुटि एक 534 ( "अंकगणित परिणाम 32 बिट से अधिक हो गई है")। जाहिर है कुछ हस्ताक्षरित पूर्णांक ओवरफ़्लो है। कुछ 'mf3216.dll' ("32-बिट से 16-बिट मेटाफाइल रूपांतरण डीएलएल") GetWinMetaFileBits द्वारा अपने निर्यात किए गए ConvertEmfToWmf फ़ंक्शन पर कॉल के दौरान त्रुटि सेट करता है, लेकिन इससे ओवरफ़्लो के संबंध में कोई भी दस्तावेज नहीं होता है। WMF सीमाओं के बारे में एकमात्र आधिकारिक दस्तावेज मुझे मिल सकता है this (इसका मुख्य बिंदु है "केवल 16 बिट निष्पादन योग्य में WMF का उपयोग करें" :))।


जैसा कि पहले उल्लेख, फर्जी "placeable हैडर" संरचना "फर्जी" मान हो सकता है और यह सही ढंग से मेटाफ़ाइल खेलने से VCL रोक सकता है। विशेष रूप से, मेटाफाइल के आयाम, क्योंकि वीसीएल उन्हें जानते हैं, अतिप्रवाह हो सकते हैं।

var 
    Header: TEnhMetaHeader; 
begin 
    DrawWMF(Rect(0, 0, Dim, Dim), 1.0, 'Original.wmf'); 
    DrawWMF(Rect(0, 0, Dim, Dim), 0.5, 'Scaled.wmf'); 

    try 
    Image1.Picture.LoadFromFile('Original.wmf'); 
    if (TMetafile(Image1.Picture.Graphic).Width < 0) or 
     (TMetafile(Image1.Picture.Graphic).Height < 0) then begin 
     GetEnhMetaFileHeader(TMetafile(Image1.Picture.Graphic).Handle, 
      SizeOf(Header), @Header); 
     TMetafile(Image1.Picture.Graphic).Width := MulDiv(Header.rclFrame.Right, 
      Header.szlDevice.cx, Header.szlMillimeters.cx * 100); 
     TMetafile(Image1.Picture.Graphic).Height := MulDiv(Header.rclFrame.Bottom, 
      Header.szlDevice.cy, Header.szlMillimeters.cy * 100); 
    end; 

    ... 
+0

तो यह मानना ​​उचित है कि 16 बिट युग मानक के रूप में, कुछ बाइनरी मेटाडाटा फ़ील्ड एक हस्ताक्षरित 16 बिट पूर्णांक की सीमा तक सीमित हो सकते हैं। उचित लगता है! –

+0

ध्यान दें कि यदि आप लिखते हैं तो टीएममेज के साथ .wmf को दिखाने का इरादा है तो सीमा कम है। –

+0

@ टॉम - यह सीमा वीसीएल द्वारा लगाई गई है। फ़ाइल पढ़ने के दौरान यह वही "प्लेटेबल हेडर" का उपयोग करता है। मेरी राय है, यह पहले स्थान पर नहीं होना चाहिए। वैसे भी, चूंकि डेल्फी के साथ पढ़ा जाएगा, 22 बाइट्स को अलग करना एक विकल्प प्रतीत नहीं होता है।यदि मैं किसी भी उपयोगी समाधान के बारे में सोच नहीं सकता तो मैं आपके उत्तर को आपके पक्ष में हटा दूंगा। इस बीच, आपको +1 .. –

1

जब दस्तावेज़ मदद नहीं करते हैं, तो स्रोत देखें :)। फ़ाइल निर्माण विफल रहता है यदि चौड़ाई या ऊंचाई बहुत बड़ी है, और फ़ाइल अमान्य हो जाती है। निम्नलिखित में मैं केवल क्षैतिज आयाम को देखता हूं, लेकिन लंबवत आयाम का इलाज समान होता है।

Vcl.Graphics में:

constructor TMetafileCanvas.CreateWithComment(AMetafile : TMetafile; 
    ReferenceDevice: HDC; const CreatedBy, Description: String); 

     FMetafile.MMWidth := MulDiv(FMetafile.Width, 
      GetDeviceCaps(RefDC, HORZSIZE) * 100, GetDeviceCaps(RefDC, HORZRES)); 

तो ReferenceDevice परिभाषित नहीं है, तो स्क्रीन (GetDC(0)) किया जाता है। मेरी मशीन क्षैतिज आकार पर 677 और क्षैतिज संकल्प के रूप में रिपोर्ट किया गया है। इस प्रकार FMetafile.MMWidth := 40000 * 67700 div 1920 (= 1410416)। चूंकि FMetaFile.MMWidth एक पूर्णांक है, इस बिंदु पर कोई समस्या नहीं है।

आगे, फ़ाइल लेखन, जो WriteWMFStream साथ किया जाता है पर देखने क्योंकि हम एक .wmf फ़ाइल पर लिखने करते हैं:

procedure TMetafile.WriteWMFStream(Stream: TStream); 
var 
    WMF: TMetafileHeader; 
    ... 
begin 
    ... 
     Inch := 96   { WMF defaults to 96 units per inch } 
    ... 
     Right := MulDiv(FWidth, WMF.Inch, HundredthMMPerInch); 
    ... 

WMF हैडर संरचना को इंगित करता है जहां चीजें दक्षिण जा रहे हैं

TMetafileHeader = record 
    Key: Longint; 
    Handle: SmallInt; 
    Box: TSmallRect; // smallint members 
    Inch: Word; 
    Reserved: Longint; 
    CheckSum: Word; 
    end; 

Box: TSmallRect फ़ील्ड smallint-आकार वाले मानों से बड़े निर्देशांक नहीं रख सकता है। दाएं Right := 1410417 * 96 div 2540 (= 53307 as smallint= -12229) के रूप में गणना की जाती है। छवि के आयामों के आयाम और wmf डेटा को फ़ाइल में 'खेला' नहीं जा सकता है।

सवाल उठता है: मैं अपनी मशीन पर किस आयाम का उपयोग कर सकता हूं?

FMetaFile.MMWidth और FMetaFile.MMHeight दोनों

MaxSmallInt * HundredthMMPerInch div UnitsPerInch or 
32767 * 2540 div 96 = 866960 

मेरी testmachine क्षैतिज प्रदर्शन आकार और संकल्प पर करने के लिए कम या बराबर होने की जरूरत है कर रहे हैं 677 और 1920 कार्यक्षेत्र प्रदर्शन आकार और संकल्प 381 और 1080 के इस प्रकार हैं मेटाफाइल का अधिकतम आयाम बन जाता है:

Horizontal: 866960 * 1920 div 67700 = 24587 
Vertical: 866960 * 1080 div 38100 = 24575 

परीक्षण द्वारा सत्यापित किया गया।


अद्यतन टिप्पणी से प्रेरित आगे की जांच पड़ताल के बाद:

32767 अप करने के लिए क्षैतिज और ऊर्ध्वाधर आयाम के साथ, मेटाफ़ाइल कुछ अनुप्रयोगों के साथ पढ़ी जा सकती है, f.ex. जिंप, यह छवि दिखाता है। संभवत: यह SmallInt के बजाय ड्राइंग के विस्तार पर word के रूप में उन कार्यक्रमों के कारण है। जीआईएमपी ने पिक्सल प्रति इंच 9 0 की सूचना दी और जब 9 6 में बदल गया (जो डेल्फी द्वारा उपयोग किया जाने वाला मूल्य है, जीआईएमपी 'गिंप संदेश: प्लग-इन क्रैश हो गया: "फाइल-डब्ल्यूएमएफ.एक्सई"

प्रक्रिया में ओपी 32767 या उससे कम के आयामों के साथ एक त्रुटि संदेश नहीं दिखाता है। हालांकि, यदि आयाम पहले से अनुमानित अधिकतम मान से अधिक आयाम है, तो चित्रण नहीं दिखाया जाता है। मेटाफाइल पढ़ने पर, उसी टीएमटाफाइलहेडर संरचना प्रकार को सहेजते समय उपयोग किया जाता है

procedure TMetafile.ReadWMFStream(Stream: TStream; Length: Longint); 
    ... 
    FWidth := MulDiv(WMF.Box.Right - WMF.Box.Left, HundredthMMPerInch, WMF.Inch); 
    FHeight := MulDiv(WMF.Box.Bottom - WMF.Box.Top, HundredthMMPerInch, WMF.Inch); 

procedure TImage.PictureChanged(Sender: TObject); 

    if AutoSize and (Picture.Width > 0) and (Picture.Height > 0) then 
    SetBounds(Left, Top, Picture.Width, Picture.Height); 

ऋणात्मक मानोंमें Paint प्रक्रिया के माध्यम से तरंग: और FWidth और FHeight नकारात्मक मूल्यों को प्राप्तफ़ंक्शन और छवि इसलिए नहीं देखी गई है।

procedure TImage.Paint; 
    ... 
     with inherited Canvas do 
     StretchDraw(DestRect, Picture.Graphic); 

DestRect नीचे

अधिकार के मान ऋणात्मक है और मैं कहना है कि वास्तविक सीमा को खोजने के लिए एक ही रास्ता दोनों क्षैतिज और ऊर्ध्वाधर आकार और संकल्प के लिए GetDeviceCaps() कहते हैं, और इसके बाद के संस्करण गणना करने के लिए है। नोट हालांकि, फ़ाइल अभी भी किसी अन्य मशीन पर डेल्फी प्रोग्राम के साथ प्रदर्शित नहीं हो सकती है। 20000 x 20000 के भीतर ड्राइंग आकार को रखना संभवतः एक सुरक्षित सीमा है।

+0

वीसीएल कॉल 'टीएमटाफाइल हैडर' वास्तव में एक ['WmfPlaceableFileHeader'] है (https://msdn.microsoft.com/en-us/library/ms534075%28v=vs.85%29.aspx) जो वास्तव में समर्थित नहीं है/एपीआई द्वारा उपयोग (टिप्पणी अनुभाग पढ़ें)। यदि आप उत्पादित फ़ाइल ('original.wmf') देखते हैं, तो आप देखेंगे कि केवल प्लेस करने योग्य हेडर लिखा गया है (22bytes)। जो मायने रखता है वह विफल रहता है, जैसा कि मैंने इसे देखा है, 'GetWinMetaFileBits' है। वीसीएल, आश्चर्यजनक रूप से, वापसी की जांच नहीं करता है, लेकिन इस सवाल में उदाहरण के साथ यह वास्तव में "अंकगणित अतिप्रवाह" के साथ विफल रहता है, जिसे मैं "रेफडीसी" से संबंधित मानता हूं। –

+0

उदाहरण में 30000 (मंद) के साथ प्रयास करें। "बॉक्स" ("बाउंडिंगबॉक्स") दाएं/नीचे के सदस्य अभी भी बहते हैं, फिर भी मेटाफाइल मान्य हैं। –

+0

@ सर्टैक वास्तव में, अभी भी जांच करने के लिए कुछ है। लेकिन, क्या फ़ाइल वास्तव में वैध है (30000 के साथ)? यह वापस पढ़ने पर नहीं दिखाता है। मैं देखता हूँ। हालांकि, इस प्रश्न के प्रयोजन के लिए, चित्रित अधिकतम मानों से कम करने के लिए ड्राइंग सीमित करना (जो अतिप्रवाह नहीं है) सही होना चाहिए, क्या आपको नहीं लगता? –

संबंधित मुद्दे