diff --git a/QRCoder/PostscriptQRCode.cs b/QRCoder/PostscriptQRCode.cs index 99f86065..03c47ef3 100644 --- a/QRCoder/PostscriptQRCode.cs +++ b/QRCoder/PostscriptQRCode.cs @@ -101,20 +101,24 @@ public string GetGraphic(Size viewBox, Color darkColor, Color lightColor, bool d var drawableModulesCount = QrCodeData.ModuleMatrix.Count - (drawQuietZones ? 0 : offset * 2); var pointsPerModule = (double)Math.Min(viewBox.Width, viewBox.Height) / (double)drawableModulesCount; - var estimatedCapacity = PS_HEADER.Length + PS_FUNCTIONS.Length + PS_FOOTER.Length + + var header = epsFormat ? EPS_HEADER : PS_HEADER; + var functions = epsFormat ? EPS_FUNCTIONS : PS_FUNCTIONS; + var footer = epsFormat ? EPS_FOOTER : PS_FOOTER; + + var estimatedCapacity = header.Length + functions.Length + footer.Length + (drawableModulesCount * drawableModulesCount * 2) + // modules (either "f " or "b ") drawableModulesCount * 3 + // newlines ("nl\n") 200; // embedded numbers var sb = new StringBuilder(estimatedCapacity); - sb.AppendFormat(CultureInfo.InvariantCulture, PS_HEADER, [ - CleanSvgVal(viewBox.Width), CleanSvgVal(pointsPerModule), - epsFormat ? "EPSF-3.0" : string.Empty + sb.AppendFormat(CultureInfo.InvariantCulture, header, [ + CleanSvgVal(viewBox.Width), CleanSvgVal(pointsPerModule) ]); - sb.AppendFormat(CultureInfo.InvariantCulture, PS_FUNCTIONS, [ + sb.AppendFormat(CultureInfo.InvariantCulture, functions, [ CleanSvgVal(darkColor.R /255.0), CleanSvgVal(darkColor.G /255.0), CleanSvgVal(darkColor.B /255.0), CleanSvgVal(lightColor.R /255.0), CleanSvgVal(lightColor.G /255.0), CleanSvgVal(lightColor.B /255.0), - drawableModulesCount + drawableModulesCount, + CleanSvgVal(viewBox.Width), CleanSvgVal(pointsPerModule) ]); for (int xi = offset; xi < offset + drawableModulesCount; xi++) @@ -127,7 +131,7 @@ public string GetGraphic(Size viewBox, Color darkColor, Color lightColor, bool d } } sb.Append('\n'); - sb.Append(PS_FOOTER); + sb.Append(footer); return sb.ToString(); } @@ -140,15 +144,17 @@ public string GetGraphic(Size viewBox, Color darkColor, Color lightColor, bool d // Note: line terminations here will encode differently based on which platform QRCoder was compiled on (CRLF vs LF); // however, PostScript interpreters should handle both equally well. + + // Standard PostScript header — includes media/page setup and setpagedevice (valid for standalone PS documents). private const string PS_HEADER = """ - %!PS-Adobe-3.0 {2} + %!PS-Adobe-3.0 %%Creator: QRCoder.NET %%Title: QRCode %%DocumentData: Clean7Bit %%Origin: 0 %%DocumentMedia: Default {0} {0} 0 () () %%BoundingBox: 0 0 {0} {0} - %%LanguageLevel: 2 + %%LanguageLevel: 2 %%Pages: 1 %%Page: 1 1 %%EndComments @@ -162,8 +168,22 @@ public string GetGraphic(Size viewBox, Color darkColor, Color lightColor, bool d """; + // EPS header — omits %%DocumentMedia, %%Pages, %%Page, and setpagedevice, all of which are + // forbidden or inappropriate in Encapsulated PostScript (Adobe Technical Note #5002). + // Constants (sz, sc) and procedure definitions are emitted in EPS_FUNCTIONS below. + private const string EPS_HEADER = """ + %!PS-Adobe-3.0 EPSF-3.0 + %%Creator: QRCoder.NET + %%Title: QRCode + %%DocumentData: Clean7Bit + %%BoundingBox: 0 0 {0} {0} + %%LanguageLevel: 2 + %%EndComments + + """; + private const string PS_FUNCTIONS = """ - %%BeginFunctions + %%BeginFunctions /csquare {{ newpath 0 0 moveto @@ -174,15 +194,15 @@ 1 0 rlineto setrgbcolor fill }} def - /f {{ + /f {{ {0} {1} {2} csquare 1 0 translate }} def - /b {{ + /b {{ 1 0 translate - }} def - /background {{ - {3} {4} {5} csquare + }} def + /background {{ + {3} {4} {5} csquare }} def /nl {{ -{6} -1 translate @@ -200,10 +220,65 @@ sc sc scale """; + // EPS variant: uses standard DSC %%BeginProlog/%%EndProlog for procedure definitions, + // %%BeginSetup/%%EndSetup for constants, and moves 0 0 moveto inside gsave so it does + // not leak the current point into the surrounding document. + private const string EPS_FUNCTIONS = """ + %%BeginProlog + 7 dict begin + /csquare {{ + newpath + 0 0 moveto + 0 1 rlineto + 1 0 rlineto + 0 -1 rlineto + closepath + setrgbcolor + fill + }} def + /f {{ + {0} {1} {2} csquare + 1 0 translate + }} def + /b {{ + 1 0 translate + }} def + /background {{ + {3} {4} {5} csquare + }} def + /nl {{ + -{6} -1 translate + }} def + %%EndProlog + %%BeginSetup + /sz {7} def + /sc {8} def + %%EndSetup + gsave + 0 0 moveto + sz sz scale + background + grestore + gsave + sc sc scale + 0 {6} 1 sub translate + + """; + + // Standard PostScript footer — showpage is required for standalone PS documents. private const string PS_FOOTER = """ %%EndBody grestore - showpage + showpage + %%EOF + + """; + + // EPS footer — showpage is forbidden in EPS files (Adobe Technical Note #5002). + // 'end' pops the private dictionary pushed in %%BeginProlog to avoid polluting the host dict stack. + private const string EPS_FOOTER = """ + grestore + end %%EOF """; diff --git a/QRCoderTests/PostscriptQRCodeRendererTests.can_render_postscript_qrcode_colors.approved.txt b/QRCoderTests/PostscriptQRCodeRendererTests.can_render_postscript_qrcode_colors.approved.txt index 60e52670..0e27a8f8 100644 --- a/QRCoderTests/PostscriptQRCodeRendererTests.can_render_postscript_qrcode_colors.approved.txt +++ b/QRCoderTests/PostscriptQRCodeRendererTests.can_render_postscript_qrcode_colors.approved.txt @@ -1,11 +1,11 @@ -%!PS-Adobe-3.0 +%!PS-Adobe-3.0 %%Creator: QRCoder.NET %%Title: QRCode %%DocumentData: Clean7Bit %%Origin: 0 %%DocumentMedia: Default 165 165 0 () () %%BoundingBox: 0 0 165 165 -%%LanguageLevel: 2 +%%LanguageLevel: 2 %%Pages: 1 %%Page: 1 1 %%EndComments @@ -16,7 +16,7 @@ %%BeginFeature: *PageSize Default << /PageSize [ sz sz ] /ImagingBBox null >> setpagedevice %%EndFeature -%%BeginFunctions +%%BeginFunctions /csquare { newpath 0 0 moveto @@ -27,15 +27,15 @@ setrgbcolor fill } def -/f { +/f { 1 0 0 csquare 1 0 translate } def -/b { +/b { 1 0 translate -} def -/background { - 0 0 1 csquare +} def +/background { + 0 0 1 csquare } def /nl { -33 -1 translate @@ -85,5 +85,5 @@ b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b nl b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b %%EndBody grestore -showpage +showpage %%EOF diff --git a/QRCoderTests/PostscriptQRCodeRendererTests.can_render_postscript_qrcode_eps.approved.txt b/QRCoderTests/PostscriptQRCodeRendererTests.can_render_postscript_qrcode_eps.approved.txt index 6a4b0410..e73fdf96 100644 --- a/QRCoderTests/PostscriptQRCodeRendererTests.can_render_postscript_qrcode_eps.approved.txt +++ b/QRCoderTests/PostscriptQRCodeRendererTests.can_render_postscript_qrcode_eps.approved.txt @@ -2,21 +2,11 @@ %%Creator: QRCoder.NET %%Title: QRCode %%DocumentData: Clean7Bit -%%Origin: 0 -%%DocumentMedia: Default 165 165 0 () () %%BoundingBox: 0 0 165 165 -%%LanguageLevel: 2 -%%Pages: 1 -%%Page: 1 1 +%%LanguageLevel: 2 %%EndComments -%%BeginConstants -/sz 165 def -/sc 5 def -%%EndConstants -%%BeginFeature: *PageSize Default -<< /PageSize [ sz sz ] /ImagingBBox null >> setpagedevice -%%EndFeature -%%BeginFunctions +%%BeginProlog +7 dict begin /csquare { newpath 0 0 moveto @@ -27,23 +17,26 @@ setrgbcolor fill } def -/f { +/f { 0 0 0 csquare 1 0 translate } def -/b { +/b { 1 0 translate -} def -/background { - 1 1 1 csquare +} def +/background { + 1 1 1 csquare } def /nl { -33 -1 translate } def -%%EndFunctions -%%BeginBody -0 0 moveto +%%EndProlog +%%BeginSetup +/sz 165 def +/sc 5 def +%%EndSetup gsave +0 0 moveto sz sz scale background grestore @@ -83,7 +76,6 @@ b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b nl b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b nl b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b nl b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b -%%EndBody grestore -showpage +end %%EOF diff --git a/QRCoderTests/PostscriptQRCodeRendererTests.can_render_postscript_qrcode_simple.approved.txt b/QRCoderTests/PostscriptQRCodeRendererTests.can_render_postscript_qrcode_simple.approved.txt index 0fd16ade..1b66e490 100644 --- a/QRCoderTests/PostscriptQRCodeRendererTests.can_render_postscript_qrcode_simple.approved.txt +++ b/QRCoderTests/PostscriptQRCodeRendererTests.can_render_postscript_qrcode_simple.approved.txt @@ -1,11 +1,11 @@ -%!PS-Adobe-3.0 +%!PS-Adobe-3.0 %%Creator: QRCoder.NET %%Title: QRCode %%DocumentData: Clean7Bit %%Origin: 0 %%DocumentMedia: Default 165 165 0 () () %%BoundingBox: 0 0 165 165 -%%LanguageLevel: 2 +%%LanguageLevel: 2 %%Pages: 1 %%Page: 1 1 %%EndComments @@ -16,7 +16,7 @@ %%BeginFeature: *PageSize Default << /PageSize [ sz sz ] /ImagingBBox null >> setpagedevice %%EndFeature -%%BeginFunctions +%%BeginFunctions /csquare { newpath 0 0 moveto @@ -27,15 +27,15 @@ setrgbcolor fill } def -/f { +/f { 0 0 0 csquare 1 0 translate } def -/b { +/b { 1 0 translate -} def -/background { - 1 1 1 csquare +} def +/background { + 1 1 1 csquare } def /nl { -33 -1 translate @@ -85,5 +85,5 @@ b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b nl b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b %%EndBody grestore -showpage +showpage %%EOF diff --git a/QRCoderTests/PostscriptQRCodeRendererTests.can_render_postscript_qrcode_size.approved.txt b/QRCoderTests/PostscriptQRCodeRendererTests.can_render_postscript_qrcode_size.approved.txt index 709bc3a1..25dc3f7d 100644 --- a/QRCoderTests/PostscriptQRCodeRendererTests.can_render_postscript_qrcode_size.approved.txt +++ b/QRCoderTests/PostscriptQRCodeRendererTests.can_render_postscript_qrcode_size.approved.txt @@ -1,11 +1,11 @@ -%!PS-Adobe-3.0 +%!PS-Adobe-3.0 %%Creator: QRCoder.NET %%Title: QRCode %%DocumentData: Clean7Bit %%Origin: 0 %%DocumentMedia: Default 33 33 0 () () %%BoundingBox: 0 0 33 33 -%%LanguageLevel: 2 +%%LanguageLevel: 2 %%Pages: 1 %%Page: 1 1 %%EndComments @@ -16,7 +16,7 @@ %%BeginFeature: *PageSize Default << /PageSize [ sz sz ] /ImagingBBox null >> setpagedevice %%EndFeature -%%BeginFunctions +%%BeginFunctions /csquare { newpath 0 0 moveto @@ -27,15 +27,15 @@ setrgbcolor fill } def -/f { +/f { 0 0 0 csquare 1 0 translate } def -/b { +/b { 1 0 translate -} def -/background { - 1 1 1 csquare +} def +/background { + 1 1 1 csquare } def /nl { -33 -1 translate @@ -85,5 +85,5 @@ b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b nl b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b %%EndBody grestore -showpage +showpage %%EOF diff --git a/QRCoderTests/PostscriptQRCodeRendererTests.can_render_postscript_qrcode_size_no_quiet_zones.approved.txt b/QRCoderTests/PostscriptQRCodeRendererTests.can_render_postscript_qrcode_size_no_quiet_zones.approved.txt index cc78b5a0..1ed6e783 100644 --- a/QRCoderTests/PostscriptQRCodeRendererTests.can_render_postscript_qrcode_size_no_quiet_zones.approved.txt +++ b/QRCoderTests/PostscriptQRCodeRendererTests.can_render_postscript_qrcode_size_no_quiet_zones.approved.txt @@ -1,11 +1,11 @@ -%!PS-Adobe-3.0 +%!PS-Adobe-3.0 %%Creator: QRCoder.NET %%Title: QRCode %%DocumentData: Clean7Bit %%Origin: 0 %%DocumentMedia: Default 50 50 0 () () %%BoundingBox: 0 0 50 50 -%%LanguageLevel: 2 +%%LanguageLevel: 2 %%Pages: 1 %%Page: 1 1 %%EndComments @@ -16,7 +16,7 @@ %%BeginFeature: *PageSize Default << /PageSize [ sz sz ] /ImagingBBox null >> setpagedevice %%EndFeature -%%BeginFunctions +%%BeginFunctions /csquare { newpath 0 0 moveto @@ -27,15 +27,15 @@ setrgbcolor fill } def -/f { +/f { 0 0 0 csquare 1 0 translate } def -/b { +/b { 1 0 translate -} def -/background { - 1 1 1 csquare +} def +/background { + 1 1 1 csquare } def /nl { -25 -1 translate @@ -77,5 +77,5 @@ f b b b b b f b f f f f b f f b b f b b f f f b b nl f f f f f f f b f b b b b f b b b f f b b b f f f %%EndBody grestore -showpage +showpage %%EOF diff --git a/QRCoderTests/TransposeVerificationTests.postscript_renderer.approved.ps b/QRCoderTests/TransposeVerificationTests.postscript_renderer.approved.ps index 52ac1b8c..389449ad 100644 Binary files a/QRCoderTests/TransposeVerificationTests.postscript_renderer.approved.ps and b/QRCoderTests/TransposeVerificationTests.postscript_renderer.approved.ps differ