|
@@ -69,7 +69,7 @@ type
|
|
TPDFPageLayout = (lSingle, lTwo, lContinuous);
|
|
TPDFPageLayout = (lSingle, lTwo, lContinuous);
|
|
TPDFUnitOfMeasure = (uomInches, uomMillimeters, uomCentimeters, uomPixels);
|
|
TPDFUnitOfMeasure = (uomInches, uomMillimeters, uomCentimeters, uomPixels);
|
|
|
|
|
|
- TPDFOption = (poOutLine, poCompressText, poCompressFonts, poCompressImages, poUseRawJPEG, poNoEmbeddedFonts, poPageOriginAtTop, poSubsetFont);
|
|
|
|
|
|
+ TPDFOption = (poOutLine, poCompressText, poCompressFonts, poCompressImages, poUseRawJPEG, poNoEmbeddedFonts, poPageOriginAtTop, poSubsetFont, poMetadataEntry);
|
|
TPDFOptions = set of TPDFOption;
|
|
TPDFOptions = set of TPDFOption;
|
|
|
|
|
|
EPDF = Class(Exception);
|
|
EPDF = Class(Exception);
|
|
@@ -327,6 +327,7 @@ type
|
|
|
|
|
|
TPDFStream = class(TPDFDocumentObject)
|
|
TPDFStream = class(TPDFDocumentObject)
|
|
private
|
|
private
|
|
|
|
+ FCompressionProhibited: Boolean;
|
|
FItems: TFPObjectList;
|
|
FItems: TFPObjectList;
|
|
protected
|
|
protected
|
|
procedure Write(const AStream: TStream); override;
|
|
procedure Write(const AStream: TStream); override;
|
|
@@ -334,6 +335,7 @@ type
|
|
public
|
|
public
|
|
constructor Create(Const ADocument : TPDFDocument; OwnsObjects : Boolean = True); overload;
|
|
constructor Create(Const ADocument : TPDFDocument; OwnsObjects : Boolean = True); overload;
|
|
destructor Destroy; override;
|
|
destructor Destroy; override;
|
|
|
|
+ property CompressionProhibited: Boolean read FCompressionProhibited write FCompressionProhibited;
|
|
end;
|
|
end;
|
|
|
|
|
|
|
|
|
|
@@ -910,6 +912,9 @@ type
|
|
Property Owner: TPDFDocument read FOwner;
|
|
Property Owner: TPDFDocument read FOwner;
|
|
end;
|
|
end;
|
|
|
|
|
|
|
|
+ TXMPStream = class(TPDFDocumentObject)
|
|
|
|
+ procedure Write(const AStream: TStream); override;
|
|
|
|
+ end;
|
|
|
|
|
|
TPDFFontNumBaseObject = class(TPDFDocumentObject)
|
|
TPDFFontNumBaseObject = class(TPDFDocumentObject)
|
|
protected
|
|
protected
|
|
@@ -1017,6 +1022,7 @@ type
|
|
function CreateContentsEntry(const APageNum: integer): integer;virtual;
|
|
function CreateContentsEntry(const APageNum: integer): integer;virtual;
|
|
function CreateCatalogEntry: integer;virtual;
|
|
function CreateCatalogEntry: integer;virtual;
|
|
procedure CreateInfoEntry;virtual;
|
|
procedure CreateInfoEntry;virtual;
|
|
|
|
+ procedure CreateMetadataEntry;virtual;
|
|
procedure CreateTrailerID;virtual;
|
|
procedure CreateTrailerID;virtual;
|
|
procedure CreatePreferencesEntry;virtual;
|
|
procedure CreatePreferencesEntry;virtual;
|
|
function CreatePagesEntry(Parent: integer): integer;virtual;
|
|
function CreatePagesEntry(Parent: integer): integer;virtual;
|
|
@@ -1200,10 +1206,32 @@ const
|
|
Var
|
|
Var
|
|
PDFFormatSettings : TFormatSettings;
|
|
PDFFormatSettings : TFormatSettings;
|
|
|
|
|
|
|
|
+//Works correctly ony with Now (problem with DST depended on time)
|
|
|
|
+//Is used only for CreationDate and it is usualy Now
|
|
|
|
+function GetLocalTZD(ISO8601: Boolean): string;
|
|
|
|
+var
|
|
|
|
+ i: Integer;
|
|
|
|
+ fmt: string;
|
|
|
|
+begin
|
|
|
|
+ if ISO8601 then
|
|
|
|
+ fmt := '%.2d:%.2d'
|
|
|
|
+ else
|
|
|
|
+ fmt := '%.2d''%.2d''';
|
|
|
|
+ i := GetLocalTimeOffset; //min
|
|
|
|
+ if i < 0 then
|
|
|
|
+ Result := '+'
|
|
|
|
+ else if i = 0 then begin
|
|
|
|
+ Result := 'Z';
|
|
|
|
+ Exit;
|
|
|
|
+ end else
|
|
|
|
+ Result := '-';
|
|
|
|
+ i := Abs(i);
|
|
|
|
+ Result := Result + Format(fmt, [i div 60, i mod 60]);
|
|
|
|
+end;
|
|
|
|
|
|
function DateToPdfDate(const ADate: TDateTime): string;
|
|
function DateToPdfDate(const ADate: TDateTime): string;
|
|
begin
|
|
begin
|
|
- Result:=FormatDateTime('"D:"yyyymmddhhnnss', ADate);
|
|
|
|
|
|
+ Result:=FormatDateTime('"D:"yyyymmddhhnnss', ADate)+GetLocalTZD(False);
|
|
end;
|
|
end;
|
|
|
|
|
|
function FormatPDFInt(const Value: integer; PadLen: integer): string;
|
|
function FormatPDFInt(const Value: integer; PadLen: integer): string;
|
|
@@ -1343,6 +1371,62 @@ begin
|
|
Result := APixels / cDefaultDPI;
|
|
Result := APixels / cDefaultDPI;
|
|
end;
|
|
end;
|
|
|
|
|
|
|
|
+{ TXMPStream }
|
|
|
|
+
|
|
|
|
+procedure TXMPStream.Write(const AStream: TStream);
|
|
|
|
+
|
|
|
|
+ procedure Add(const Tag, Value: string);
|
|
|
|
+ begin
|
|
|
|
+ WriteString('<'+Tag+'>', AStream);
|
|
|
|
+ WriteString(Value, AStream);
|
|
|
|
+ WriteString('</'+Tag+'>'+CRLF, AStream);
|
|
|
|
+ end;
|
|
|
|
+
|
|
|
|
+ function DateToISO8601Date(t: TDateTime): string;
|
|
|
|
+ begin
|
|
|
|
+ Result := FormatDateTime('yyyy-mm-dd"T"hh:nn:ss', t) + GetLocalTZD(True);
|
|
|
|
+ end;
|
|
|
|
+
|
|
|
|
+var
|
|
|
|
+ i: integer;
|
|
|
|
+const
|
|
|
|
+ NBSP: UnicodeChar = UnicodeChar($FEFF);
|
|
|
|
+begin
|
|
|
|
+ WriteString('<?xpacket begin="'+UnicodeCharToString(@NBSP)+'" id="W5M0MpCehiHzreSzNTczkc9d"?>'+CRLF, AStream);
|
|
|
|
+ WriteString('<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">'+CRLF, AStream);
|
|
|
|
+ WriteString('<rdf:Description rdf:about=""', AStream);
|
|
|
|
+ WriteString(' xmlns:dc="http://purl.org/dc/elements/1.1/"', AStream);
|
|
|
|
+ WriteString(' xmlns:xmp="http://ns.adobe.com/xap/1.0/"', AStream);
|
|
|
|
+ WriteString(' xmlns:pdf="http://ns.adobe.com/pdf/1.3/"', AStream);
|
|
|
|
+ WriteString(' xmlns:pdfaid="http://www.aiim.org/pdfa/ns/id/"', AStream);
|
|
|
|
+ WriteString('>'+CRLF, AStream);
|
|
|
|
+
|
|
|
|
+ //Native metadata
|
|
|
|
+ if (Document.Infos.Title <> '') or (Document.Infos.Author <> '') then begin
|
|
|
|
+ if Document.Infos.Title <> '' then
|
|
|
|
+ Add('dc:title', '<rdf:Alt><rdf:li xml:lang="x-default">'+Document.Infos.Title+'</rdf:li></rdf:Alt>');
|
|
|
|
+ if Document.Infos.Author <> '' then
|
|
|
|
+ Add('dc:creator', Document.Infos.Author);
|
|
|
|
+ end;
|
|
|
|
+ if Document.Infos.ApplicationName <> '' then
|
|
|
|
+ Add('xmp:CreatorTool', Document.Infos.ApplicationName);
|
|
|
|
+ if Document.Infos.CreationDate <> 0 then
|
|
|
|
+ Add('xmp:CreateDate', DateToISO8601Date(Document.Infos.CreationDate));
|
|
|
|
+ Add('pdf:Producer', Document.Infos.Producer);
|
|
|
|
+ //PDF/A
|
|
|
|
+ Add('pdfaid:part', '1');
|
|
|
|
+ Add('pdfaid:conformance', 'B');
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+ WriteString('</rdf:Description>'+CRLF, AStream);
|
|
|
|
+ WriteString('</rdf:RDF>'+CRLF, AStream);
|
|
|
|
+
|
|
|
|
+ //Recomended whitespace padding for inplace editing
|
|
|
|
+ for i := 1 to 5 do
|
|
|
|
+ WriteString(' '+CRLF, AStream);
|
|
|
|
+ WriteString('<?xpacket end="w"?>', AStream);
|
|
|
|
+end;
|
|
|
|
+
|
|
{ TPDFRawHexString }
|
|
{ TPDFRawHexString }
|
|
|
|
|
|
procedure TPDFRawHexString.Write(const AStream: TStream);
|
|
procedure TPDFRawHexString.Write(const AStream: TStream);
|
|
@@ -4330,7 +4414,7 @@ begin
|
|
X.FStream.Write(M);
|
|
X.FStream.Write(M);
|
|
d := M.Size;
|
|
d := M.Size;
|
|
|
|
|
|
- if poCompressText in Options then
|
|
|
|
|
|
+ if (poCompressText in Options) and not X.FStream.CompressionProhibited then
|
|
begin
|
|
begin
|
|
MCompressed := TMemoryStream.Create;
|
|
MCompressed := TMemoryStream.Create;
|
|
CompressStream(M, MCompressed);
|
|
CompressStream(M, MCompressed);
|
|
@@ -4346,7 +4430,7 @@ begin
|
|
CurrentColor:='';
|
|
CurrentColor:='';
|
|
CurrentWidth:='';
|
|
CurrentWidth:='';
|
|
TPDFObject.WriteString(CRLF+'stream'+CRLF, AStream);
|
|
TPDFObject.WriteString(CRLF+'stream'+CRLF, AStream);
|
|
- if poCompressText in Options then
|
|
|
|
|
|
+ if (poCompressText in Options) and not X.FStream.CompressionProhibited then
|
|
begin
|
|
begin
|
|
MCompressed.Position := 0;
|
|
MCompressed.Position := 0;
|
|
MCompressed.SaveToStream(AStream);
|
|
MCompressed.SaveToStream(AStream);
|
|
@@ -4409,7 +4493,22 @@ begin
|
|
if Infos.ApplicationName <> '' then
|
|
if Infos.ApplicationName <> '' then
|
|
IDict.AddString('Creator',Infos.ApplicationName);
|
|
IDict.AddString('Creator',Infos.ApplicationName);
|
|
IDict.AddString('Producer',Infos.Producer);
|
|
IDict.AddString('Producer',Infos.Producer);
|
|
- IDict.AddString('CreationDate',DateToPdfDate(Infos.CreationDate));
|
|
|
|
|
|
+ if Infos.CreationDate <> 0 then
|
|
|
|
+ IDict.AddString('CreationDate',DateToPdfDate(Infos.CreationDate));
|
|
|
|
+end;
|
|
|
|
+
|
|
|
|
+procedure TPDFDocument.CreateMetadataEntry;
|
|
|
|
+var
|
|
|
|
+ lXRef: TPDFXRef;
|
|
|
|
+begin
|
|
|
|
+ lXRef := CreateGlobalXRef;
|
|
|
|
+ lXRef.Dict.AddName('Subtype', 'XML');
|
|
|
|
+ lXRef.Dict.AddName('Type','Metadata');
|
|
|
|
+ lXRef.FStream := CreateStream(True);
|
|
|
|
+ lXRef.FStream.AddItem(TXMPStream.Create(self));
|
|
|
|
+ lXRef.FStream.CompressionProhibited := True;
|
|
|
|
+
|
|
|
|
+ GlobalXRefs[Catalogue].Dict.AddReference('Metadata', GLobalXRefCount-1)
|
|
end;
|
|
end;
|
|
|
|
|
|
procedure TPDFDocument.CreateTrailerID;
|
|
procedure TPDFDocument.CreateTrailerID;
|
|
@@ -4958,6 +5057,8 @@ begin
|
|
CreateTrailer;
|
|
CreateTrailer;
|
|
FCatalogue:=CreateCatalogEntry;
|
|
FCatalogue:=CreateCatalogEntry;
|
|
CreateInfoEntry;
|
|
CreateInfoEntry;
|
|
|
|
+ if poMetadataEntry in Options then
|
|
|
|
+ CreateMetadataEntry;
|
|
CreateTrailerID;
|
|
CreateTrailerID;
|
|
CreatePreferencesEntry;
|
|
CreatePreferencesEntry;
|
|
if (FontDirectory = '') then
|
|
if (FontDirectory = '') then
|