OSDN Git Service

binding with libharu.
[putex/putex.git] / src / texsourc / lib / libhpdf / src / hpdf_pdfa.c
1 /*
2  * << Haru Free PDF Library >> -- hpdf_pdfa.c
3  *
4  * URL: http://libharu.org
5  *
6  * Copyright (c) 1999-2006 Takeshi Kanno <takeshi_kanno@est.hi-ho.ne.jp>
7  * Copyright (c) 2007-2009 Antony Dovgal <tony@daylessday.org>
8  *
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.
15  *
16  */
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
20 #endif
21
22 #include <time.h>
23 #include "hpdf_utils.h"
24 #include "hpdf.h"
25 #include <string.h>
26
27
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>"
42
43 /*
44  * Convert date in PDF specific format: D:YYYYMMDDHHmmSS
45  * to XMP value in format YYYY-MM-DDTHH:mm:SS+offH:offMin
46  */
47 HPDF_STATUS ConvertDateToXMDate(HPDF_Stream stream, const char *pDate)
48 {
49     HPDF_STATUS ret;
50
51     if(pDate==NULL) return HPDF_INVALID_PARAMETER;
52     if(strlen(pDate)<16) return HPDF_INVALID_PARAMETER;
53     if(pDate[0]!='D'||
54         pDate[1]!=':') return HPDF_INVALID_PARAMETER;
55     pDate+=2;
56     /* Copy YYYY */
57     ret = HPDF_Stream_Write(stream, (const HPDF_BYTE*)pDate, 4);
58     if (ret != HPDF_OK)
59         return ret;
60     pDate+=4;
61     /* Write -MM */
62     ret = HPDF_Stream_Write(stream, (const HPDF_BYTE*)"-", 1);
63     if (ret != HPDF_OK)
64         return ret;
65     ret = HPDF_Stream_Write(stream, (const HPDF_BYTE*)pDate, 2);
66     if (ret != HPDF_OK)
67         return ret;
68     pDate+=2;
69     /* Write -DD */
70     ret = HPDF_Stream_Write(stream, (const HPDF_BYTE*)"-", 1);
71     if (ret != HPDF_OK)
72         return ret;
73     ret = HPDF_Stream_Write(stream, (const HPDF_BYTE*)pDate, 2);
74     if (ret != HPDF_OK)
75         return ret;
76     pDate+=2;
77     /* Write THH */
78     ret = HPDF_Stream_Write(stream, (const HPDF_BYTE*)"T", 1);
79     if (ret != HPDF_OK)
80         return ret;
81     ret = HPDF_Stream_Write(stream, (const HPDF_BYTE*)pDate, 2);
82     if (ret != HPDF_OK)
83         return ret;
84     pDate+=2;
85     /* Write :mm */
86     ret = HPDF_Stream_Write(stream, (const HPDF_BYTE*)":", 1);
87     if (ret != HPDF_OK)
88         return ret;
89     ret = HPDF_Stream_Write(stream, (const HPDF_BYTE*)pDate, 2);
90     if (ret != HPDF_OK)
91         return ret;
92     pDate+=2;
93     /* Write :SS */
94     ret = HPDF_Stream_Write(stream, (const HPDF_BYTE*)":", 1);
95     if (ret != HPDF_OK)
96         return ret;
97     ret = HPDF_Stream_Write(stream, (const HPDF_BYTE*)pDate, 2);
98     if (ret != HPDF_OK)
99         return ret;
100     pDate+=2;
101     /* Write +... */
102     if(pDate[0]==0) {
103         ret = HPDF_Stream_Write(stream, (const HPDF_BYTE*)"+0:00", 5);
104         return ret;
105     }
106     if(pDate[0]=='+'||pDate[0]=='-') {
107         ret = HPDF_Stream_Write(stream, (const HPDF_BYTE*)pDate, strlen(pDate));
108         return ret;        
109     }
110     return HPDF_SetError (stream->error, HPDF_INVALID_PARAMETER, 0);
111 }
112
113 /* Write XMP Metadata for PDF/A */
114
115 HPDF_STATUS
116 HPDF_PDFA_SetPDFAConformance (HPDF_Doc pdf,HPDF_PDFAType pdfatype)
117 {
118     HPDF_OutputIntent xmp;
119     HPDF_STATUS ret;
120     const char *info = NULL;
121     
122     if (!HPDF_HasDoc(pdf)) {
123       return HPDF_INVALID_DOCUMENT;
124     }
125     
126     xmp = HPDF_DictStream_New(pdf->mmgr,pdf->xref);
127     if (!xmp) {
128       return HPDF_INVALID_STREAM;
129     }
130     
131     HPDF_Dict_AddName(xmp,"Type","Metadata");
132     HPDF_Dict_AddName(xmp,"SubType","XML");
133
134     ret = HPDF_OK;
135     ret += HPDF_Stream_WriteStr(xmp->stream, XMP_HEADER);
136     
137     ret += HPDF_Stream_WriteStr(xmp->stream, XMP_PRODUCER_STARTTAG);
138    
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);
142     
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);
150     
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);
155     
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);
160     
161     ret += HPDF_Stream_WriteStr(xmp->stream, XMP_INFODATA_ENDTAG);
162     
163     switch(pdfatype) {
164       case HPDF_PDFA_1A:
165         ret += HPDF_Stream_WriteStr(xmp->stream, XMP_PDFA1A);
166         break;
167       case HPDF_PDFA_1B:
168         ret += HPDF_Stream_WriteStr(xmp->stream, XMP_PDFA1B);
169         break;
170     }
171     ret += HPDF_Stream_WriteStr(xmp->stream, XMP_FOOTER);
172
173     if (ret != HPDF_OK) {
174       return HPDF_INVALID_STREAM;
175     }
176     
177     if ((ret = HPDF_Dict_Add(pdf->catalog, "Metadata", xmp)) != HPDF_OK) {
178       return ret;
179     }
180     
181     return HPDF_PDFA_GenerateID(pdf);
182 }
183
184 /* Generate an ID for the trailer dict, PDF/A needs this. 
185    TODO: Better algorithm for generate unique ID.
186 */
187 HPDF_STATUS
188 HPDF_PDFA_GenerateID(HPDF_Doc pdf)
189 {
190     HPDF_Array id;
191     HPDF_BYTE *currentTime;
192     HPDF_BYTE idkey[HPDF_MD5_KEY_LEN];
193     HPDF_MD5_CTX md5_ctx;
194     time_t ltime; 
195
196     ltime = time(NULL); 
197     currentTime = (HPDF_BYTE *)ctime(&ltime);
198         
199     id = HPDF_Dict_GetItem(pdf->trailer, "ID", HPDF_OCLASS_ARRAY);
200     if (!id) {
201        id = HPDF_Array_New(pdf->mmgr);
202
203        if (!id || HPDF_Dict_Add(pdf->trailer, "ID", id) != HPDF_OK)
204          return pdf->error.error_no;
205        
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);
210        
211        if (HPDF_Array_Add (id, HPDF_Binary_New (pdf->mmgr, idkey, HPDF_MD5_KEY_LEN)) != HPDF_OK)
212          return pdf->error.error_no;
213
214        if (HPDF_Array_Add (id, HPDF_Binary_New (pdf->mmgr,idkey,HPDF_MD5_KEY_LEN)) != HPDF_OK)
215          return pdf->error.error_no;
216     
217        return HPDF_OK;
218     }
219     
220     return HPDF_OK;
221 }
222
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
227  *
228  * How to use:
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);
234  *    if(ret!=HPDF_OK) {
235  *      HPDF_Dict_Free(icc);
236  *      return false;
237  *    }
238  *
239  * 2. Call this function
240  */
241
242 HPDF_STATUS
243 HPDF_PDFA_AppendOutputIntents(HPDF_Doc pdf, const char *iccname, HPDF_Dict iccdict)
244 {
245     HPDF_Array intents;
246     HPDF_Dict intent;
247     HPDF_STATUS ret;
248     if (!HPDF_HasDoc (pdf))
249         return HPDF_INVALID_DOCUMENT;
250
251     /* prepare intent */
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);
256         return ret;
257     }
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);
266         return ret;
267     }
268
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);
273         if (intents) {
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);
278             }
279         }
280     }
281
282     HPDF_Array_Add(intents,intent);
283     return HPDF_Error_GetDetailCode (&pdf->error);
284 }