2 * << Haru Free PDF Library >> -- hpdf_pdfa.c
4 * URL: http://libharu.org
6 * Copyright (c) 1999-2006 Takeshi Kanno <takeshi_kanno@est.hi-ho.ne.jp>
7 * Copyright (c) 2007-2009 Antony Dovgal <tony@daylessday.org>
9 * Permission to use, copy, modify, distribute and sell this software
10 * and its documentation for any purpose is hereby granted without fee,
11 * provided that the above copyright notice appear in all copies and
12 * that both that copyright notice and this permission notice appear
13 * in supporting documentation.
14 * It is provided "as is" without express or implied warranty.
17 /* This is used to avoid warnings on 'ctime' when compiling in MSVC 9 */
18 #ifndef _CRT_SECURE_NO_WARNINGS
19 #define _CRT_SECURE_NO_WARNINGS
23 #include "hpdf_utils.h"
28 #define XMP_HEADER "<?xpacket begin='' id='W5M0MpCehiHzreSzNTczkc9d'?><?adobe-xap-filters esc=\"CRLF\"?><x:xmpmeta xmlns:x='adobe:ns:meta/' x:xmptk='XMP toolkit 2.9.1-13, framework 1.6'><rdf:RDF xmlns:rdf='http://www.w3.org/1999/02/22-rdf-syntax-ns#' xmlns:iX='http://ns.adobe.com/iX/1.0/'>"
29 #define XMP_FOOTER "</rdf:RDF></x:xmpmeta><?xpacket end='w'?>"
30 #define XMP_PDFA1A "<rdf:Description rdf:about='' xmlns:pdfaid='http://www.aiim.org/pdfa/ns/id/' pdfaid:part='1' pdfaid:conformance='A'/>"
31 #define XMP_PDFA1B "<rdf:Description rdf:about='' xmlns:pdfaid='http://www.aiim.org/pdfa/ns/id/' pdfaid:part='1' pdfaid:conformance='B'/>"
32 #define XMP_PRODUCER_STARTTAG "<rdf:Description rdf:about='' xmlns:pdf='http://ns.adobe.com/pdf/1.3/' pdf:Producer='"
33 #define XMP_PRODUCER_ENDTAG "'/>"
34 #define XMP_INFODATA_STARTTAG "<rdf:Description rdf:about='' xmlns:xmp='http://ns.adobe.com/xap/1.0/'>"
35 #define XMP_INFODATA_ENDTAG "</rdf:Description>"
36 #define XMP_CREATE_DATE_STARTTAG "<xmp:CreateDate>"
37 #define XMP_CREATE_DATE_ENDTAG "</xmp:CreateDate>"
38 #define XMP_MOD_DATE_STARTTAG "<xmp:ModifyDate>"
39 #define XMP_MOD_DATE_ENDTAG "</xmp:ModifyDate>"
40 #define XMP_CREATORTOOL_STARTTAG "<xmp:CreatorTool>"
41 #define XMP_CREATORTOOL_ENDTAG "</xmp:CreatorTool>"
44 * Convert date in PDF specific format: D:YYYYMMDDHHmmSS
45 * to XMP value in format YYYY-MM-DDTHH:mm:SS+offH:offMin
47 HPDF_STATUS ConvertDateToXMDate(HPDF_Stream stream, const char *pDate)
51 if(pDate==NULL) return HPDF_INVALID_PARAMETER;
52 if(strlen(pDate)<16) return HPDF_INVALID_PARAMETER;
54 pDate[1]!=':') return HPDF_INVALID_PARAMETER;
57 ret = HPDF_Stream_Write(stream, (const HPDF_BYTE*)pDate, 4);
62 ret = HPDF_Stream_Write(stream, (const HPDF_BYTE*)"-", 1);
65 ret = HPDF_Stream_Write(stream, (const HPDF_BYTE*)pDate, 2);
70 ret = HPDF_Stream_Write(stream, (const HPDF_BYTE*)"-", 1);
73 ret = HPDF_Stream_Write(stream, (const HPDF_BYTE*)pDate, 2);
78 ret = HPDF_Stream_Write(stream, (const HPDF_BYTE*)"T", 1);
81 ret = HPDF_Stream_Write(stream, (const HPDF_BYTE*)pDate, 2);
86 ret = HPDF_Stream_Write(stream, (const HPDF_BYTE*)":", 1);
89 ret = HPDF_Stream_Write(stream, (const HPDF_BYTE*)pDate, 2);
94 ret = HPDF_Stream_Write(stream, (const HPDF_BYTE*)":", 1);
97 ret = HPDF_Stream_Write(stream, (const HPDF_BYTE*)pDate, 2);
103 ret = HPDF_Stream_Write(stream, (const HPDF_BYTE*)"+0:00", 5);
106 if(pDate[0]=='+'||pDate[0]=='-') {
107 ret = HPDF_Stream_Write(stream, (const HPDF_BYTE*)pDate, strlen(pDate));
110 return HPDF_SetError (stream->error, HPDF_INVALID_PARAMETER, 0);
113 /* Write XMP Metadata for PDF/A */
116 HPDF_PDFA_SetPDFAConformance (HPDF_Doc pdf,HPDF_PDFAType pdfatype)
118 HPDF_OutputIntent xmp;
120 const char *info = NULL;
122 if (!HPDF_HasDoc(pdf)) {
123 return HPDF_INVALID_DOCUMENT;
126 xmp = HPDF_DictStream_New(pdf->mmgr,pdf->xref);
128 return HPDF_INVALID_STREAM;
131 HPDF_Dict_AddName(xmp,"Type","Metadata");
132 HPDF_Dict_AddName(xmp,"SubType","XML");
135 ret += HPDF_Stream_WriteStr(xmp->stream, XMP_HEADER);
137 ret += HPDF_Stream_WriteStr(xmp->stream, XMP_PRODUCER_STARTTAG);
139 info = (const char *)HPDF_GetInfoAttr(pdf, HPDF_INFO_PRODUCER);
140 ret += HPDF_Stream_WriteStr(xmp->stream, info);
141 ret += HPDF_Stream_WriteStr(xmp->stream, XMP_PRODUCER_ENDTAG);
143 /* Add CreateDate, ModifyDate, and CreatorTool */
144 ret += HPDF_Stream_WriteStr(xmp->stream, XMP_INFODATA_STARTTAG);
145 info = (const char *)HPDF_GetInfoAttr(pdf, HPDF_INFO_CREATION_DATE);
146 ret += HPDF_Stream_WriteStr(xmp->stream, XMP_CREATE_DATE_STARTTAG);
147 /* Convert date to XMP compatible format */
148 ret += ConvertDateToXMDate(xmp->stream, info);
149 ret += HPDF_Stream_WriteStr(xmp->stream, XMP_CREATE_DATE_ENDTAG);
151 info = (const char *)HPDF_GetInfoAttr(pdf, HPDF_INFO_MOD_DATE);
152 ret += HPDF_Stream_WriteStr(xmp->stream, XMP_MOD_DATE_STARTTAG);
153 ret += ConvertDateToXMDate(xmp->stream, info);
154 ret += HPDF_Stream_WriteStr(xmp->stream, XMP_MOD_DATE_ENDTAG);
156 info = (const char *)HPDF_GetInfoAttr(pdf, HPDF_INFO_CREATOR);
157 ret += HPDF_Stream_WriteStr(xmp->stream, XMP_CREATORTOOL_STARTTAG);
158 ret += HPDF_Stream_WriteStr(xmp->stream, info);
159 ret += HPDF_Stream_WriteStr(xmp->stream, XMP_CREATORTOOL_ENDTAG);
161 ret += HPDF_Stream_WriteStr(xmp->stream, XMP_INFODATA_ENDTAG);
165 ret += HPDF_Stream_WriteStr(xmp->stream, XMP_PDFA1A);
168 ret += HPDF_Stream_WriteStr(xmp->stream, XMP_PDFA1B);
171 ret += HPDF_Stream_WriteStr(xmp->stream, XMP_FOOTER);
173 if (ret != HPDF_OK) {
174 return HPDF_INVALID_STREAM;
177 if ((ret = HPDF_Dict_Add(pdf->catalog, "Metadata", xmp)) != HPDF_OK) {
181 return HPDF_PDFA_GenerateID(pdf);
184 /* Generate an ID for the trailer dict, PDF/A needs this.
185 TODO: Better algorithm for generate unique ID.
188 HPDF_PDFA_GenerateID(HPDF_Doc pdf)
191 HPDF_BYTE *currentTime;
192 HPDF_BYTE idkey[HPDF_MD5_KEY_LEN];
193 HPDF_MD5_CTX md5_ctx;
197 currentTime = (HPDF_BYTE *)ctime(<ime);
199 id = HPDF_Dict_GetItem(pdf->trailer, "ID", HPDF_OCLASS_ARRAY);
201 id = HPDF_Array_New(pdf->mmgr);
203 if (!id || HPDF_Dict_Add(pdf->trailer, "ID", id) != HPDF_OK)
204 return pdf->error.error_no;
206 HPDF_MD5Init(&md5_ctx);
207 HPDF_MD5Update(&md5_ctx, (HPDF_BYTE *) "libHaru", sizeof("libHaru") - 1);
208 HPDF_MD5Update(&md5_ctx, currentTime, HPDF_StrLen((const char *)currentTime, -1));
209 HPDF_MD5Final(idkey, &md5_ctx);
211 if (HPDF_Array_Add (id, HPDF_Binary_New (pdf->mmgr, idkey, HPDF_MD5_KEY_LEN)) != HPDF_OK)
212 return pdf->error.error_no;
214 if (HPDF_Array_Add (id, HPDF_Binary_New (pdf->mmgr,idkey,HPDF_MD5_KEY_LEN)) != HPDF_OK)
215 return pdf->error.error_no;
223 /* Function to add one outputintents to the PDF
224 * iccname - name of default ICC profile
225 * iccdict - dictionary containing number of components
226 * and stream with ICC profile
229 * 1. Create dictionary with ICC profile
230 * HPDF_Dict icc = HPDF_DictStream_New (pDoc->mmgr, pDoc->xref);
231 * if(icc==NULL) return false;
232 * HPDF_Dict_AddNumber (icc, "N", 3);
233 * HPDF_STATUS ret = HPDF_Stream_Write (icc->stream, (const HPDF_BYTE *)pICCData, dwICCSize);
235 * HPDF_Dict_Free(icc);
239 * 2. Call this function
243 HPDF_PDFA_AppendOutputIntents(HPDF_Doc pdf, const char *iccname, HPDF_Dict iccdict)
248 if (!HPDF_HasDoc (pdf))
249 return HPDF_INVALID_DOCUMENT;
252 intent = HPDF_Dict_New (pdf->mmgr);
253 ret = HPDF_Xref_Add (pdf->xref, intent);
254 if ( ret != HPDF_OK) {
255 HPDF_Dict_Free(intent);
258 ret += HPDF_Dict_AddName (intent, "Type", "OutputIntent");
259 ret += HPDF_Dict_AddName (intent, "S", "GTS_PDFA1");
260 ret += HPDF_Dict_Add (intent, "OutputConditionIdentifier", HPDF_String_New (pdf->mmgr, iccname, NULL));
261 ret += HPDF_Dict_Add (intent, "OutputCondition", HPDF_String_New (pdf->mmgr, iccname,NULL));
262 ret += HPDF_Dict_Add (intent, "Info", HPDF_String_New (pdf->mmgr, iccname, NULL));
263 ret += HPDF_Dict_Add (intent, "DestOutputProfile ", iccdict);
264 if ( ret != HPDF_OK) {
265 HPDF_Dict_Free(intent);
269 /* Copied from HPDF_AddIntent - not public function */
270 intents = HPDF_Dict_GetItem (pdf->catalog, "OutputIntents", HPDF_OCLASS_ARRAY);
271 if (intents == NULL) {
272 intents = HPDF_Array_New (pdf->mmgr);
274 HPDF_STATUS ret = HPDF_Dict_Add (pdf->catalog, "OutputIntents", intents);
275 if (ret != HPDF_OK) {
276 HPDF_CheckError (&pdf->error);
277 return HPDF_Error_GetDetailCode (&pdf->error);
282 HPDF_Array_Add(intents,intent);
283 return HPDF_Error_GetDetailCode (&pdf->error);