PDFlibPas превръща enhanced metafile в истинско PDF съдържание на страница запис по запис, вместо да ги растеризира, което е това, което държи импортирана диаграма или CAD чертеж остър при всяко мащабиране; Този конвертор е около 6500 реда и беше написан срещу VCL, така че когато библиотеката придоби Free Pascal цел, той беше класифициран като непреносим и беше замазан със заглушка; Класификацията беше грешна, а начинът, по който беше грешна, е полезен урок за това как да одитирате зависимост, преди да решите да пренапишете около нея
Реалната VCL повърхност на тези 6500 реда се оказа малка: клас за bitmap, използван за неговия пикселен формат, записване в поток, handle, canvas и scanlines; клас за metafile, използван за неговата ширина, височина и handle; и типът цвят с две константи; Всяко едно от тях вече се предоставяше от собствения graphics модул на библиотеката, който съществува именно така, че non-VCL компилацията да има еквиваленти; Конверторът изобщо не беше блокиран от VCL; Той беше блокиран от Free Pascal Windows модула
Разделяне по оста, от която кодът реално зависи
Така че промяната не беше нова имплементация; Тя беше един условен израз: от „компилирай заглушката, когато се компилира без VCL“ към „компилирай заглушката, когато не се компилира за Windows“; Това е правилната ос, а изясняването защо прави разликата очевидна; Enhanced metafile е Windows контейнер; Конверторът е парсер за Windows GDI записи от горе до долу; Дали хост приложението използва VCL, друг набор от widget-и или изобщо никакъв набор няма нищо общо с това дали тези записи могат да бъдат интерпретирани; дали целта е Windows има всичко общо с това
Последиците от избора на правилната ос изтичат безплатно; C++Builder компилациите, които отдефинират символа на Windows платформата в тази библиотека, запазват хвърлящата заглушка и се държат точно както преди; macOS запазва заглушката, правилно, защото там няма GDI записи за парсване; Delphi VCL компилациите са недокоснати; И Windows компилация с non-VCL набор от widget-и придобива векторен EMF импорт като страничен ефект, което никой не е трябвало да имплементира; Условен израз, съгласуван с истинската зависимост, превръща работата по платформата в промяна от един ред; условен израз, съгласуван с грешната, я превръща в преписване, което никога не влиза в графика
Пропастта на Free Pascal беше декларации, не логика
Това, което действително липсваше, бяха Win32 декларациите, които Delphi Windows модулът предоставя, а Free Pascal не; Събирането им в единичен compatibility модул, вместо разпръскване на условни изрази из конвертора, запази парсера четим; Списъкът е поучителен, защото показва колко неравномерно е покритието на headers между двете RTL: 113 константи за типове metafile записи, два флага за разширено текстово извеждане, три константи за режим на gradient fill, тип указател за handle таблица, псевдоними за gradient vertex и primitive записите и три типа записи, които Free Pascal изобщо не декларира, обхващащи alpha blending, transparent blitting и режим на управление на цветовете
Нито едно от това не е интересно поотделно; Всичко трябва да е право, преди парсерът да се компилира, а compatibility модулът е естествен дом, защото може да се диферира срещу header документацията като единица
Онова, което мълшиво рисува грешната картина
Две от тези декларации не са просто липсващи, те са налични и грешни за тази цел, а това е частта, която си заслужава да запомните, дори никога да не докоснете metafile
Free Pascal декларира записа за създаване на четка със вградената run-time структура на четката, а записа за разширеното перо с вградената run-time структура на перото; И двете run-time структури декларират своя hatch член като цяло число с размера на указател, защото в живо GDI извикване този член може да носи handle; Metafile обаче винаги съхранява 32-битовата форма, защото оформлението на записа е част от сериализирания файлов формат и не се променя с битовата ширина на процеса
При 32-битови компилации двете съвпадат и нищо не се случва; На Win64 членът с размера на указател е осем байта, докато файлът има четири, така че всяко поле след hatch члена се чете от грешния offset; Няма изключение, няма грешка при парсване и няма предупреждение; Metafile просто се изобразява грешно: цветове от грешните байтове, ширини на перото от грешните байтове и картина, която изглежда като дефект при изобразяване, а не като дефект на struct оформлението; Delphi доставя изрично 32-битови варианти на двете структури именно поради тази причина, а compatibility модулът ги редекларира по същия начин
// Грешно на Win64: Hatch е с размера на указател, файлът съхранява
// 32 бита, а всяко следващо поле се измества с четири байта без грешка
type
TLogBrushRuntime = record
lbStyle: UINT;
lbColor: COLORREF;
lbHatch: ULONG_PTR; // 8 байта в 64-битов процес
end;
// Правилно: сериализираното оформление, фиксирана ширина независимо
// от битовата ширина
type
TLogBrush32 = record
lbStyle: UINT;
lbColor: COLORREF;
lbHatch: DWORD; // винаги 4 байта, както е съхранено в metafile
end;
Общото правило: всяка структура, която се появява едновременно като run-time API аргумент и като сериализирано оформление на полета се нуждае от две декларации, а сериализираната трябва да използва фиксирани по ширина типове навсякъде; Членове с размера на указател във файлов формат са винаги дефект, чакащ 64-битова компилация
Разлики в сигнатурите принадлежат на wrapper, не на всяко място на извикване
Останалите разлики бяха обикновени несъответствия в сигнатурите, а начинът да се погълнат е forwarding wrapper, а не условен израз на всяко от местата за извикване; Функцията за съчетаване на трансформации приема указатели под Free Pascal, докато Delphi приема reference параметри, така че wrapper-ът приема референции и предава адреси; Той също копира и двата изходни аргумента в локални променливи първо, защото конверторът има места за извикване, където матрицата-приемник е едновременно един от източниците, и подаването на един и същ адрес два пъти на функция, която пише, докато чете, произвежда трансформация, която е тънко грешна по начин, който се проявява само върху завъртяно съдържание
function CombineTransformCompat(var Dest: TXForm;
const A, B: TXForm): BOOL;
var
SrcA, SrcB: TXForm;
begin
// Копиране първо: извикващите законно подават Dest като A или B
SrcA := A;
SrcB := B;
{$IFDEF FPC}
Result := Windows.CombineTransform(@Dest, @SrcA, @SrcB);
{$ELSE}
Result := Windows.CombineTransform(Dest, SrcA, SrcB);
{$ENDIF}
end;
Типовете rectangle и point са другият случай; Free Pascal третира metafile записите за rectangle и point като отделни типове от общите graphics такива, така че осем места за присвояване се нуждаеха от изричен cast между записи с идентично оформление; И двата компилатора приемат cast формата, така че тези места не носят изобщо условен израз, което си струва малко грознота
Какво това променя за внедряване на Free Pascal
Векторният EMF импорт работи на Windows под Free Pascal, произвеждайки същото съдържание на страница като Delphi компилацията: paths като paths, градиенти като pattern съдържание, текст като текст; Извън Windows растерният път остава отговорът и това е ограничение на формата, а не на преноса; Координатното и clipping състоянието, в което конверторът захранва, е описано в статията за content stream CTM и clipping тракера, а векторните примитиви, които той излъчва, са разгледани в векторна графика, шейдъри и градиенти
Ако одитирате собствения си код за същата възможност, полезното упражнение е онова, което стартира това: избройте членовете, които действително използвате от фреймуърка, от който мислите, че зависите; Отговорът често е много по-кратък, отколкото списъкът за импорт подсказва, а истинското ограничение обикновено е някъде съвсем другаде; Пътищата за импорт, базирани на device context, изобщо са описани в статията за print preview и device context, а покритието на платформи и toolchain е изброено на продуктовата страница losLab PDF Developer Library