Skip to content

Commit 9098793

Browse files
committed
Added PdfByteQRCode renderer
1 parent 5fcc701 commit 9098793

File tree

1 file changed

+237
-0
lines changed

1 file changed

+237
-0
lines changed

QRCoder/PdfByteQRCode.cs

Lines changed: 237 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,237 @@
1+
#if NETFRAMEWORK || NETSTANDARD2_0
2+
using System;
3+
using System.Collections.Generic;
4+
using System.Drawing.Imaging;
5+
using System.Globalization;
6+
using System.IO;
7+
using System.Linq;
8+
using static QRCoder.QRCodeGenerator;
9+
10+
/* This renderer is inspired by RemusVasii: https://github.com/codebude/QRCoder/issues/223 */
11+
namespace QRCoder
12+
{
13+
14+
// ReSharper disable once InconsistentNaming
15+
public class PdfByteQRCode : AbstractQRCode, IDisposable
16+
{
17+
private readonly byte[] pdfBinaryComment = new byte[] { 0x25, 0xe2, 0xe3, 0xcf, 0xd3 };
18+
19+
/// <summary>
20+
/// Constructor without params to be used in COM Objects connections
21+
/// </summary>
22+
public PdfByteQRCode() { }
23+
24+
public PdfByteQRCode(QRCodeData data) : base(data) { }
25+
26+
/// <summary>
27+
/// Creates a PDF document with a black & white QR code
28+
/// </summary>
29+
/// <param name="pixelsPerModule"></param>
30+
/// <returns></returns>
31+
public byte[] GetGraphic(int pixelsPerModule)
32+
{
33+
return GetGraphic(pixelsPerModule, "#000000", "#ffffff");
34+
}
35+
36+
/// <summary>
37+
/// Takes hexadecimal color string #000000 and returns byte[]{ 0, 0, 0 }
38+
/// </summary>
39+
/// <param name="colorString">Color in HEX format like #ffffff</param>
40+
/// <returns></returns>
41+
private byte[] HexColorToByteArray(string colorString)
42+
{
43+
if (colorString.StartsWith("#"))
44+
colorString = colorString.Substring(1);
45+
byte[] byteColor = new byte[colorString.Length / 2];
46+
for (int i = 0; i < byteColor.Length; i++)
47+
byteColor[i] = byte.Parse(colorString.Substring(i * 2, 2), NumberStyles.HexNumber, CultureInfo.InvariantCulture);
48+
return byteColor;
49+
}
50+
51+
/// <summary>
52+
/// Creates a PDF document with given colors DPI and quality
53+
/// </summary>
54+
/// <param name="pixelsPerModule"></param>
55+
/// <param name="darkColorHtmlHex"></param>
56+
/// <param name="lightColorHtmlHex"></param>
57+
/// <param name="dpi"></param>
58+
/// <param name="jpgQuality"></param>
59+
/// <returns></returns>
60+
public byte[] GetGraphic(int pixelsPerModule, string darkColorHtmlHex, string lightColorHtmlHex, int dpi = 150, long jpgQuality = 85)
61+
{
62+
byte[] jpgArray = null, pngArray = null;
63+
var imgSize = QrCodeData.ModuleMatrix.Count * pixelsPerModule;
64+
var pdfMediaSize = (imgSize * 72 / dpi).ToString(CultureInfo.InvariantCulture);
65+
66+
//Get QR code image
67+
using (var qrCode = new PngByteQRCode(QrCodeData))
68+
{
69+
pngArray = qrCode.GetGraphic(pixelsPerModule, HexColorToByteArray(darkColorHtmlHex), HexColorToByteArray(lightColorHtmlHex));
70+
}
71+
72+
//Create image and transofrm to JPG
73+
using (var msPng = new MemoryStream())
74+
{
75+
msPng.Write(pngArray, 0, pngArray.Length);
76+
var img = System.Drawing.Image.FromStream(msPng);
77+
using (var msJpeg = new MemoryStream())
78+
{
79+
// Create JPEG with specified quality
80+
var jpgImageCodecInfo = ImageCodecInfo.GetImageEncoders().First(x => x.MimeType == "image/jpeg");
81+
var jpgEncoderParameters = new EncoderParameters(1) {
82+
Param = new EncoderParameter[]{ new EncoderParameter(Encoder.Quality, jpgQuality) }
83+
};
84+
img.Save(msJpeg, jpgImageCodecInfo, jpgEncoderParameters);
85+
jpgArray = msJpeg.ToArray();
86+
}
87+
}
88+
89+
//Create PDF document
90+
using (var stream = new MemoryStream())
91+
{
92+
var writer = new StreamWriter(stream, System.Text.Encoding.GetEncoding("ASCII"));
93+
94+
var xrefs = new List<long>();
95+
96+
writer.Write("%PDF-1.5\r\n");
97+
writer.Flush();
98+
99+
stream.Write(pdfBinaryComment, 0, pdfBinaryComment.Length);
100+
writer.WriteLine();
101+
102+
writer.Flush();
103+
xrefs.Add(stream.Position);
104+
105+
writer.Write(
106+
xrefs.Count.ToString() + " 0 obj\r\n" +
107+
"<<\r\n" +
108+
"/Type /Catalog\r\n" +
109+
"/Pages 2 0 R\r\n" +
110+
">>\r\n" +
111+
"endobj\r\n"
112+
);
113+
114+
writer.Flush();
115+
xrefs.Add(stream.Position);
116+
117+
writer.Write(
118+
xrefs.Count.ToString() + " 0 obj\r\n" +
119+
"<<\r\n" +
120+
"/Count 1\r\n" +
121+
"/Kids [ <<\r\n" +
122+
"/Type /Page\r\n" +
123+
"/Parent 2 0 R\r\n" +
124+
"/MediaBox [0 0 " + pdfMediaSize + " " + pdfMediaSize + "]\r\n" +
125+
"/Resources << /ProcSet [ /PDF /ImageC ]\r\n" +
126+
"/XObject << /Im1 4 0 R >> >>\r\n" +
127+
"/Contents 3 0 R\r\n" +
128+
">> ]\r\n" +
129+
">>\r\n" +
130+
"endobj\r\n"
131+
);
132+
133+
var X = "q\r\n" +
134+
pdfMediaSize + " 0 0 " + pdfMediaSize + " 0 0 cm\r\n" +
135+
"/Im1 Do\r\n" +
136+
"Q";
137+
138+
writer.Flush();
139+
xrefs.Add(stream.Position);
140+
141+
writer.Write(
142+
xrefs.Count.ToString() + " 0 obj\r\n" +
143+
"<< /Length " + X.Length.ToString() + " >>\r\n" +
144+
"stream\r\n" +
145+
X + "endstream\r\n" +
146+
"endobj\r\n"
147+
);
148+
149+
writer.Flush();
150+
xrefs.Add(stream.Position);
151+
152+
writer.Write(
153+
xrefs.Count.ToString() + " 0 obj\r\n" +
154+
"<<\r\n" +
155+
"/Name /Im1\r\n" +
156+
"/Type /XObject\r\n" +
157+
"/Subtype /Image\r\n" +
158+
"/Width " + imgSize.ToString() + "/Height " + imgSize.ToString() + "/Length 5 0 R\r\n" +
159+
"/Filter /DCTDecode\r\n" +
160+
"/ColorSpace /DeviceRGB\r\n" +
161+
"/BitsPerComponent 8\r\n" +
162+
">>\r\n" +
163+
"stream\r\n"
164+
);
165+
writer.Flush();
166+
stream.Write(jpgArray, 0, jpgArray.Length);
167+
writer.Write(
168+
"\r\n" +
169+
"endstream\r\n" +
170+
"endobj\r\n"
171+
);
172+
173+
writer.Flush();
174+
xrefs.Add(stream.Position);
175+
176+
writer.Write(
177+
xrefs.Count.ToString() + " 0 obj\r\n" +
178+
jpgArray.Length.ToString() + " endobj\r\n"
179+
);
180+
181+
writer.Flush();
182+
var startxref = stream.Position;
183+
184+
writer.Write(
185+
"xref\r\n" +
186+
"0 " + (xrefs.Count + 1).ToString() + "\r\n" +
187+
"0000000000 65535 f\r\n"
188+
);
189+
190+
foreach (var refValue in xrefs)
191+
writer.Write(refValue.ToString("0000000000") + " 00000 n\r\n");
192+
193+
writer.Write(
194+
"trailer\r\n" +
195+
"<<\r\n" +
196+
"/Size " + (xrefs.Count + 1).ToString() + "\r\n" +
197+
"/Root 1 0 R\r\n" +
198+
">>\r\n" +
199+
"startxref\r\n" +
200+
startxref.ToString() + "\r\n" +
201+
"%%EOF"
202+
);
203+
204+
writer.Flush();
205+
206+
stream.Position = 0;
207+
208+
return stream.ToArray();
209+
}
210+
}
211+
}
212+
213+
public static class PdfByteQRCodeHelper
214+
{
215+
public static byte[] GetQRCode(string plainText, int pixelsPerModule, string darkColorHtmlHex,
216+
string lightColorHtmlHex, ECCLevel eccLevel, bool forceUtf8 = false, bool utf8BOM = false,
217+
EciMode eciMode = EciMode.Default, int requestedVersion = -1)
218+
{
219+
using (var qrGenerator = new QRCodeGenerator())
220+
using (
221+
var qrCodeData = qrGenerator.CreateQrCode(plainText, eccLevel, forceUtf8, utf8BOM, eciMode,
222+
requestedVersion))
223+
using (var qrCode = new PdfByteQRCode(qrCodeData))
224+
return qrCode.GetGraphic(pixelsPerModule, darkColorHtmlHex, lightColorHtmlHex);
225+
}
226+
227+
public static byte[] GetQRCode(string txt, ECCLevel eccLevel, int size)
228+
{
229+
using (var qrGen = new QRCodeGenerator())
230+
using (var qrCode = qrGen.CreateQrCode(txt, eccLevel))
231+
using (var qrBmp = new PdfByteQRCode(qrCode))
232+
return qrBmp.GetGraphic(size);
233+
234+
}
235+
}
236+
}
237+
#endif

0 commit comments

Comments
 (0)