pdf.js (2989B)
1 function header() { 2 return '%PDF-1.3'; 3 } 4 5 function eof() { 6 return '%%EOF\n'; 7 } 8 9 function recCatalog() { 10 return ( 11 `1 0 obj 12 << /Type /Catalog 13 /Pages 2 0 R 14 >> 15 endobj`); 16 } 17 18 function recPages(count) { 19 let kidsIds = []; 20 for (let i = 0; i < count; i++) { 21 kidsIds.push(`${i * 3 + 3} 0 R`); 22 } 23 kidsIds = kidsIds.join(' '); 24 return ( 25 `2 0 obj 26 << /Type /Pages 27 /Kids [ ${kidsIds} ] 28 /Count ${count} 29 >> 30 endobj`); 31 } 32 33 function recPage(serialNum, width, height) { 34 const pageRecId = serialNum * 3 + 3; 35 const contentRecId = pageRecId + 1; 36 const imageRecId = pageRecId + 2; 37 return ( 38 `${pageRecId} 0 obj 39 << /Type /Page 40 /Parent 2 0 R 41 /MediaBox [0 0 ${width} ${height}] 42 /Contents ${contentRecId} 0 R 43 /Rotate ${width > height ? 90 : 0} 44 /Resources << 45 /XObject << /Im${serialNum} ${imageRecId} 0 R >> 46 /ProcSet [ /PDF /Text /ImageC ] 47 >> 48 >> 49 endobj`); 50 } 51 52 function recContent(serialNum, width, height) { 53 const pageRecId = serialNum * 3 + 3; 54 const contentRecId = pageRecId + 1; 55 const contents = ( 56 `q 57 ${width} 0 0 ${height} 0 0 cm 58 /Im${serialNum} Do 59 Q `); 60 return ( 61 `${contentRecId} 0 obj 62 << 63 /Length ${contents.length + 1} 64 >> 65 stream 66 ${contents} 67 endstream 68 endobj`); 69 } 70 71 function recImage(serialNum, widthPixels, heightPixels, data) { 72 const pageRecId = serialNum * 3 + 3; 73 const imageRecId = pageRecId + 2; 74 75 return ( 76 `${imageRecId} 0 obj 77 << /Type /XObject 78 /Subtype /Image 79 /Name /Im${serialNum} 80 /Filter [ /DCTDecode ] 81 /Width ${widthPixels} 82 /Height ${heightPixels} 83 /ColorSpace /DeviceRGB 84 /BitsPerComponent 8 85 /Length ${data.length} 86 >> 87 stream 88 ${data} 89 endstream 90 endobj`); 91 } 92 93 function recTrailer(recOffsets, currentOffset) { 94 const trailer = []; 95 trailer.push( 96 `xref 97 0 ${recOffsets.length + 1} 98 0000000000 65535 f `); 99 const zeroes = '0000000000'; 100 for (let offset of recOffsets) { 101 offset = offset.toString(); 102 let padding = zeroes.substr(offset.length); 103 trailer.push(`${padding}${offset} 00000 n `); 104 } 105 106 trailer.push( 107 `trailer 108 << /Root 1 0 R 109 /Size ${recOffsets.length + 1} 110 >> 111 startxref 112 ${currentOffset}`); 113 return trailer.join('\n'); 114 } 115 116 function makePdf(imagesInfo, resolution) { 117 let offset = 0; 118 const offsets = []; 119 let pdf = []; 120 121 function addRec(s, skipOffset) { 122 pdf.push(s); 123 if (!skipOffset) { 124 offsets.push(offset); 125 } 126 offset += s.length; 127 } 128 129 addRec(header(), true); 130 addRec(recCatalog()); 131 addRec(recPages(imagesInfo.length)); 132 for (let [i, {data, width, height}] of imagesInfo.entries()) { 133 let widthPoints = width / resolution * 72, 134 heightPoints = height / resolution * 72; 135 addRec(recPage(i, widthPoints, heightPoints)); 136 addRec(recContent(i, widthPoints, heightPoints)); 137 addRec(recImage(i, width, height, data)); 138 } 139 addRec(recTrailer(offsets, offset), true); 140 addRec(eof(), true); 141 142 pdf = pdf.join('\n'); 143 return pdf; 144 } 145 146 export {makePdf}; 147