Hola
Ante todo gracias por dejarme formar parte de vuestro foro ya que, gracias a vosotros he podido encontrar
la guía que me ha llevado a la solución correcta y poder compartirla con vosotros.
Aunque la solución que dais puede que funcione, no es la correcta ya que lo único que
hace es disimular el problema pero éste permanece latente y te puede explotar por otro lado.
El problema es que, desde la última actualización de W10(1803) las fuentes parece ser que son más ricas en cuanto a información(probablemente para trabajar mejor con pantallas de alta resolución) y la información recogida por la función GetFontData para, por ejemplo, la fuente Arial se ha triplicado.
Por ejemplo en la penúltima versión de W10, antes de la actualización "maldita"(1803), GetFontData devolvía unos 350.000 bytes de información y ahora devuelve aproximadamente 1.500.000. El buffer donde se recoge la información de
GetFontData es inicializado como una PByteArray con SetLength() y, desgraciadamente esta función parece sufrir un desbordamiento cuando intenta reservar tanta memoria.
Yo he hecho todo el proceso con QuickReport 5.02 y C++Builder 6 pero seguro que se puede aplicar a mas versiones de Delphi
Esto se puede encontrar dentro del módulo QRPDFFilt
procedure MakeTTFont
dentro de la sección 'var'
Canviar
por:
Esto nos permitirá tratar la infomación como un puntero a una matriz de Bytes y, en vez de
usar el gestionador de memoria de Delphi para generar matrices (arrays) de longitud variable (y que
es lo que falla realmente cuando se le pide una cantidad tan astronómica de Bytes mediante SetLength()), utilizar otro más potente del
propio Windows: CoTaskMemAlloc. Éste seguro que no se quedará corto a la hora de guardar memoria ya que es el que
utiliza Windows para su tecnología COM
Para poder usar CoTaskMemAlloc necesitamos poner en la clausula
uses de QRPDFFilt 'ActiveX' (naturalmente sin las comillas).
Yo lo he puesto después de Db, pero podría ser en cualquier parte de dicha cláusula
uses
Código Delphi
[-] Windows, Classes, Controls, StdCtrls, SysUtils, Graphics, Buttons, Forms, ExtCtrls, Dialogs, Printers, Db,
ActiveX,
{$IFDEF QRBDE} DBTables, {$ENDIF}
ComCtrls, qrexport, QRPrntr, QuickRpt, QR5Const, QRCtrls, grimgctrl, pdfconst, LZW;
Y ahora viene lo bueno:
Hacia la línea de código 1628 del módulo QRPDFFilt, donde se hace la reserva de memoria para recuperar la informació de 'GetFontData'
SUBSTITUIR:
POR ESTAS DOS LÍNEAS
Código Delphi
[-]Buff:=CoTaskMemAlloc(FSize);
if Buff=nil then raise EOutOfMemory.Create('Memoria insuficiente")
Como ahora la memoria reservada no se libera sola, hemos de llamar a CoTraskMemFree. Para ello,
en la clausula 'finally'(linea 1627 aproximadamente) que aparece más abajo (justo debajo de SetEncoding) añadir la siguiente línea:
Código Delphi
[-] SetEncoding;
finally
if Buff<>nil then CoTaskMemFree(Buff);
TmpImage.Free;
end;
Como ahora pasamos un PByteArray a las funciones cvtInt y cvtDWord hemos de canviar sus argumentos quedando las
declaraciones de la siguiente manera (el cuerpo de las funciones queda exactamente igual)
Código Delphi
[-]function cvtDWord(Buf: PByteArray; P: Integer) : DWORD;
begin
Result:=(256*256*256*Buf[P])+(256*256*Buf[P+1])+(256*Buf[P+2])+Buf[P+3];
end;
function cvtInt(Buf: PByteArray; P: Integer) : Integer;
begin
Result:=(256*Buf[P])+(Buf[P+1]);
end;
Y ya está!!! ahora sí que podeis generar los pdf's sin miedo a desbordamientos de memoria. Además como utilizamos el mismo gestionador
de memoria de COM, el cual está gestionado por Windows no creo que esto vuelva a dar problemas aunque haya más actualizaciones
y la información de las fuentes se vuelva a triplicar.
Por cierto, en el módulo 'pdfobjs.pas' aparece otra llamada a 'GetFontData' en el procedimiento AnalyseTTFOnt, bajando hacia la línia 728 podemos encontrar de nuevo la sentencia SetLength(buff,fsize), justo antes de la llamada a GetFontData os recomiendo que hagais el mismo proceso en este módulo y la cambiéis por CoTaskMalloc. Aseguraros que liberáis la memoria alojada por CoTaskMalloc con CoTaskMemFree en un bloque finally que tendreis que añadir
expresamente al final de 'AnalyseTtFont' . Yo lo he puesto despues de la sentencia
Código Delphi
[-] encoding := encoding + '>>';
finally
if Buff<>nil then CoTaskMemFree(Buff);
end;