View Issue Details

IDProjectCategoryView StatusLast Update
0034187FPCPackagespublic2019-07-24 12:04
ReporterDomenico MammolaAssigned ToMichael Van Canneyt 
PrioritynormalSeverityminorReproducibilityN/A
Status resolvedResolutionfixed 
Product Version3.1.1Product Build 
Target Version3.2.0Fixed in Version3.3.1 
Summary0034187: A small step toward support for PDF/A
DescriptionHello,
I need to add support for PDF/A to fcl-pdf package so I have tried to extend what is already available in the package as the development of this feature was already started in trunk.
I am not an expert of PDF/A ISO standard but with the help of the documentation, Vera pdf checker and LibreOffice sample PDF/A files I have found some small changes in the xml metadata generation that could be applied to existing code to increase compliance to PDF/A standard.
Please have a look to the enclosed patch file. I hope it could be useful.

Domenico Mammola
TagsNo tags attached.
Fixed in Revision39842
FPCOldBugId
FPCTarget
Attached Files
  • fppdf.patch (3,552 bytes)
    633a634
    >     FKeywords: String;
    640a642
    >     Property Keywords : String read FKeywords write FKeywords;
    1388a1391,1417
    > function XMLEscape(const Data: string): string;
    > var
    >   iPos, i: Integer;
    > 
    >   procedure Encode(const AStr: string);
    >   begin
    >     Move(AStr[1], result[iPos], Length(AStr) * SizeOf(Char));
    >     Inc(iPos, Length(AStr));
    >   end;
    > 
    > begin
    >   SetLength(result, Length(Data) * 6);
    >   iPos := 1;
    >   for i := 1 to length(Data) do
    >     case Data[i] of
    >       '<': Encode('&lt;');
    >       '>': Encode('&gt;');
    >       '&': Encode('&amp;');
    >       '"': Encode('&quot;');
    >     else
    >       result[iPos] := Data[i];
    >       Inc(iPos);
    >     end;
    >   SetLength(result, iPos - 1);
    > end;
    > 
    > 
    1430a1460
    >   WriteString('<x:xmpmeta xmlns:x="adobe:ns:meta/">'+CRLF, AStream);
    1431a1462
    > 
    1433,1435d1463
    <   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);
    1437a1466,1469
    >   //PDF/A
    >   Add('pdfaid:part', '1');
    >   Add('pdfaid:conformance', 'B');
    >   WriteString('</rdf:Description>'+CRLF, AStream);
    1439,1445c1471,1481
    <   //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;
    ---
    >   WriteString('<rdf:Description rdf:about=""', AStream);
    >   WriteString(' xmlns:pdf="http://ns.adobe.com/pdf/1.3/"', AStream);
    >   WriteString('>'+CRLF, AStream);
    >   Add('pdf:Producer', XMLEscape(Document.Infos.Producer));
    >   if Document.Infos.Keywords <> '' then
    >     Add('pdf:Keywords', XMLEscape(Document.Infos.Keywords));
    >   WriteString('</rdf:Description>'+CRLF, AStream);
    > 
    >   WriteString('<rdf:Description rdf:about=""', AStream);
    >   WriteString(' xmlns:xmp="http://ns.adobe.com/xap/1.0/"', AStream);
    >   WriteString('>'+CRLF, AStream);
    1447c1483
    <     Add('xmp:CreatorTool', Document.Infos.ApplicationName);
    ---
    >     Add('xmp:CreatorTool', XMLEscape(Document.Infos.ApplicationName));
    1450,1453c1486
    <   Add('pdf:Producer', Document.Infos.Producer);
    <   //PDF/A
    <   Add('pdfaid:part', '1');
    <   Add('pdfaid:conformance', 'B');
    ---
    >   WriteString('</rdf:Description>'+CRLF, AStream);
    1454a1488,1492
    >   if (Document.Infos.Title <> '') or (Document.Infos.Author <> '') then
    >   begin
    >     WriteString('<rdf:Description rdf:about=""', AStream);
    >     WriteString(' xmlns:dc="http://purl.org/dc/elements/1.1/"', AStream);
    >     WriteString('>'+CRLF, AStream);
    1455a1494,1497
    >     if Document.Infos.Title <> '' then
    >       Add('dc:title', '<rdf:Alt><rdf:li xml:lang="x-default">'+XMLEscape(Document.Infos.Title)+'</rdf:li></rdf:Alt>');
    >     if Document.Infos.Author <> '' then
    >       Add('dc:creator', '<rdf:Seq><rdf:li>'+ XMLEscape(Document.Infos.Author) + '</rdf:li></rdf:Seq>');
    1456a1499,1500
    >   end;
    > 
    1457a1502
    >   WriteString('</x:xmpmeta>'+CRLF, AStream);
    1460c1505
    <   for i := 1 to 5 do
    ---
    >   for i := 1 to 21 do
    4182a4228
    >   FKeywords:= '';
    4532a4579,4580
    >   if Infos.Keywords <> '' then
    >     IDict.AddString('Keywords', Infos.Keywords);
    4540d4587
    <   lXRef.Dict.AddName('Subtype', 'XML');
    4541a4589
    >   lXRef.Dict.AddName('Subtype', 'XML');
    
    fppdf.patch (3,552 bytes)

Activities

Domenico Mammola

2018-08-27 17:38

reporter  

fppdf.patch (3,552 bytes)
633a634
>     FKeywords: String;
640a642
>     Property Keywords : String read FKeywords write FKeywords;
1388a1391,1417
> function XMLEscape(const Data: string): string;
> var
>   iPos, i: Integer;
> 
>   procedure Encode(const AStr: string);
>   begin
>     Move(AStr[1], result[iPos], Length(AStr) * SizeOf(Char));
>     Inc(iPos, Length(AStr));
>   end;
> 
> begin
>   SetLength(result, Length(Data) * 6);
>   iPos := 1;
>   for i := 1 to length(Data) do
>     case Data[i] of
>       '<': Encode('&lt;');
>       '>': Encode('&gt;');
>       '&': Encode('&amp;');
>       '"': Encode('&quot;');
>     else
>       result[iPos] := Data[i];
>       Inc(iPos);
>     end;
>   SetLength(result, iPos - 1);
> end;
> 
> 
1430a1460
>   WriteString('<x:xmpmeta xmlns:x="adobe:ns:meta/">'+CRLF, AStream);
1431a1462
> 
1433,1435d1463
<   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);
1437a1466,1469
>   //PDF/A
>   Add('pdfaid:part', '1');
>   Add('pdfaid:conformance', 'B');
>   WriteString('</rdf:Description>'+CRLF, AStream);
1439,1445c1471,1481
<   //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;
---
>   WriteString('<rdf:Description rdf:about=""', AStream);
>   WriteString(' xmlns:pdf="http://ns.adobe.com/pdf/1.3/"', AStream);
>   WriteString('>'+CRLF, AStream);
>   Add('pdf:Producer', XMLEscape(Document.Infos.Producer));
>   if Document.Infos.Keywords <> '' then
>     Add('pdf:Keywords', XMLEscape(Document.Infos.Keywords));
>   WriteString('</rdf:Description>'+CRLF, AStream);
> 
>   WriteString('<rdf:Description rdf:about=""', AStream);
>   WriteString(' xmlns:xmp="http://ns.adobe.com/xap/1.0/"', AStream);
>   WriteString('>'+CRLF, AStream);
1447c1483
<     Add('xmp:CreatorTool', Document.Infos.ApplicationName);
---
>     Add('xmp:CreatorTool', XMLEscape(Document.Infos.ApplicationName));
1450,1453c1486
<   Add('pdf:Producer', Document.Infos.Producer);
<   //PDF/A
<   Add('pdfaid:part', '1');
<   Add('pdfaid:conformance', 'B');
---
>   WriteString('</rdf:Description>'+CRLF, AStream);
1454a1488,1492
>   if (Document.Infos.Title <> '') or (Document.Infos.Author <> '') then
>   begin
>     WriteString('<rdf:Description rdf:about=""', AStream);
>     WriteString(' xmlns:dc="http://purl.org/dc/elements/1.1/"', AStream);
>     WriteString('>'+CRLF, AStream);
1455a1494,1497
>     if Document.Infos.Title <> '' then
>       Add('dc:title', '<rdf:Alt><rdf:li xml:lang="x-default">'+XMLEscape(Document.Infos.Title)+'</rdf:li></rdf:Alt>');
>     if Document.Infos.Author <> '' then
>       Add('dc:creator', '<rdf:Seq><rdf:li>'+ XMLEscape(Document.Infos.Author) + '</rdf:li></rdf:Seq>');
1456a1499,1500
>   end;
> 
1457a1502
>   WriteString('</x:xmpmeta>'+CRLF, AStream);
1460c1505
<   for i := 1 to 5 do
---
>   for i := 1 to 21 do
4182a4228
>   FKeywords:= '';
4532a4579,4580
>   if Infos.Keywords <> '' then
>     IDict.AddString('Keywords', Infos.Keywords);
4540d4587
<   lXRef.Dict.AddName('Subtype', 'XML');
4541a4589
>   lXRef.Dict.AddName('Subtype', 'XML');
fppdf.patch (3,552 bytes)

Michael Van Canneyt

2018-09-30 11:09

administrator   ~0111102

Thank you very much for the patch. I have applied it, and adapted the example program to include keywords.

Issue History

Date Modified Username Field Change
2018-08-27 17:38 Domenico Mammola New Issue
2018-08-27 17:38 Domenico Mammola File Added: fppdf.patch
2018-08-28 01:47 Michael Van Canneyt Assigned To => Michael Van Canneyt
2018-08-28 01:47 Michael Van Canneyt Status new => assigned
2018-09-30 11:09 Michael Van Canneyt Fixed in Revision => 39842
2018-09-30 11:09 Michael Van Canneyt Note Added: 0111102
2018-09-30 11:09 Michael Van Canneyt Status assigned => resolved
2018-09-30 11:09 Michael Van Canneyt Fixed in Version => 3.3.1
2018-09-30 11:09 Michael Van Canneyt Resolution open => fixed
2018-09-30 11:09 Michael Van Canneyt Target Version => 3.2.0