Loading the parity record
Fetching the measured run — the pinned upstream oracle, every symbol and every case.
Fetching the measured run — the pinned upstream oracle, every symbol and every case.
Parity / PDFKit
Every number on this page was produced by running both implementations over the same cases: the real pdfkit package pinned at 0.15.1 answers first, and its answer is the expectation the Go port is held to. Nothing is a hand-written expectation, so a new upstream release re-scores the port on its own. See PDFKit for the port's own documentation. Source: github.com/malcolmston/pdfkit.
Not a generic diagram: every node below names an artefact of this harness — the pinned package it installed, the runner files it started, the case files it streamed, and the counts it wrote out.
| Case group | Cases | Match | Mismatch | Group parity |
|---|---|---|---|---|
| vector | 25 | 0 | 25 | 0.0% |
| text | 18 | 1 | 17 | 5.6% |
| annotations | 12 | 0 | 12 | 0.0% |
| pages | 11 | 3 | 8 | 27.3% |
| gradients | 9 | 2 | 7 | 22.2% |
| images | 9 | 2 | 7 | 22.2% |
| transform | 7 | 0 | 7 | 0.0% |
| transparency | 6 | 0 | 6 | 0.0% |
| clipping | 5 | 0 | 5 | 0.0% |
| forms | 5 | 0 | 5 | 0.0% |
| encryption | 4 | 0 | 4 | 0.0% |
| metadata | 4 | 0 | 4 | 0.0% |
| outline | 4 | 0 | 4 | 0.0% |
Every exported symbol of the upstream package, and what the port offers for it. The upstream list is derived mechanically, never from a README: node -e "
const P = require('pdfkit').prototype;
const skip = new Set(['constructor','toString']);
console.log(Object.getOwnPropertyNames(P)
.filter(n => !n.startsWith('_') && !skip.has(n)).sort().join('\n'));
". A symbol with no case is untested, never a match.
| Upstream symbol | Go symbol | Status | Cases | Note |
|---|---|---|---|---|
| addContent | — | missing | — | raw operator injection; the port has no escape hatch into the content stream |
| addNamedDestination | Document.AddNamedDest | differs | annot-named-dest-and-link | /XYZ left is 0 upstream, null in the port |
| addNamedEmbeddedFile | — | missing | — | named embedded-file tree |
| addNamedJavaScript | — | missing | — | document-level JavaScript |
| addPage | Document.AddPage | match | page-single-a4, page-letter, page-sizes-all, page-custom-size, page-landscape, page-many | geometry agrees; the margin/margins options are separately missing |
| addStructure | — | missing | — | tagged-PDF structure tree |
| annotate | — | missing | — | the generic annotation writer every annotation helper builds on |
| appendXML | — | missing | — | appends to the XMP packet; Document.SetXMP replaces it wholesale (listed as extra) |
| arc | — | missing | — | no arc primitive; only SVGPath's A command flattens arcs |
| bezierCurveTo | Page.CurveTo | match | vec-bezier | |
| bufferedPageRange | — | missing | — | the port has no page buffering to interrogate |
| circle | Page.Circle | differs | vec-circle, clip-circle, grad-radial-fill | same four-Bezier geometry traced in the opposite winding direction |
| clip | Page.Clip, Page.ClipEvenOdd | match | clip-rect, clip-even-odd | |
| closePath | Page.ClosePath | match | vec-close-path | |
| continueOnNewPage | — | missing | — | tagged-PDF marked-content continuation |
| createStructParentTreeNextKey | — | missing | — | tagged PDF |
| currentLineHeight | Font.LineHeight | untested | — | measurement only; never reaches the content stream, so no case can compare it |
| dash | Page.SetDash | differs | vec-dash-simple, vec-dash-phase, vec-dash-zero-length | the port accepts zero and negative dash lengths that upstream rejects |
| ellipse | Page.Ellipse | differs | vec-ellipse | winding direction reversed, as circle |
| ellipseAnnotation | — | missing | annot-ellipse | |
| end | Document.Bytes, Document.Write | match | every case | serialisation; exercised by all 119 cases |
| endAcroForm | (inside Document.Write) | differs | form-text-field, form-mixed | the port omits /Border on widgets and writes /V as a name for buttons |
| endMarkedContent | — | missing | — | tagged PDF |
| endMarkings | — | missing | — | tagged PDF |
| endMetadata | — | missing | enc-rc4-128-user-password | upstream emits an XMP /Metadata stream for encrypted documents; the port emits none |
| endOutline | (inside Document.Write) | differs | outline-flat, outline-nested | destination mode /Fit upstream vs /XYZ in the port |
| endPageMarkings | — | missing | — | tagged PDF |
| file | — | missing | — | embedded file streams |
| fileAnnotation | — | missing | — | file-attachment annotation |
| fill | Page.Fill, Page.FillEvenOdd | match | vec-rect-fill, vec-even-odd-fill | |
| fillAndStroke | Page.FillStroke | match | vec-rect-fill-stroke | |
| fillColor | Page.SetFillColor, Page.SetFillCMYK, Page.SetFillPattern | match | vec-rect-fill, vec-cmyk-colours, text-fill-colour | plain colours agree once the cs+scn vs rg/k spelling is folded; gradients and patterns are scored on their own rows |
| fillOpacity | Page.SetAlpha | differs | alpha-fill-opacity, alpha-reused-state | the port allocates a fresh ExtGState per call instead of reusing an identical one |
| flushPages | — | missing | — | the port has no streaming page flush |
| font | Page.SetFont, pdfkit.StandardFont | differs | text-standard-14 | upstream stamps /Encoding /WinAnsiEncoding on Symbol and ZapfDingbats too; the port correctly omits it |
| fontSize | Page.SetFont (size argument) | match | text-sizes | the Tf size operand agrees; the kerning difference is scored on text/widthOfString |
| formAnnotation | — | missing | — | the generic widget-annotation writer |
| formCheckbox | Page.AddCheckbox | differs | form-checkbox, form-mixed | /V is a name in the port and a string upstream; no /Border |
| formCombo | — | missing | — | combo box |
| formField | — | missing | — | field groups and hierarchies; *FormField is opaque |
| formList | — | missing | — | list box |
| formPushButton | Page.AddPushButton | differs | form-push-button, form-mixed | no /Border; caption is not otherwise configurable |
| formRadioButton | — | missing | — | radio button |
| formText | Page.AddTextField | differs | form-text-field, form-text-field-configured | no /Border; none of upstream's options (align, fontSize, multiline, required, borderColor, backgroundColor, …) can be expressed |
| getMarkInfoDictionary | — | missing | — | tagged PDF |
| getStructParentTree | — | missing | — | tagged PDF |
| getStructTreeRoot | — | missing | — | tagged PDF |
| goTo | Page.AddLinkToDest | differs | annot-named-dest-and-link | the port omits the /F 4 (Print) annotation flag |
| heightOfString | Font.HeightOfString | untested | — | measurement only |
| highlight | — | missing | annot-highlight | |
| image | Page.DrawImage | differs | img-png-rgb, img-png-rgba, img-png-gray, img-jpeg, img-under-transform | JPEG placement and DCTDecode agree exactly; PNG is re-encoded as raw FlateDecode RGB instead of passing the IDAT through with /DecodeParms /Predictor 15 |
| initColor | — | missing | — | mixin initialiser; the port has no explicit initialisation step |
| initFonts | — | missing | — | mixin initialiser |
| initForm | — | missing | form-text-field | upstream requires it before any field; the port creates the AcroForm implicitly |
| initImages | — | missing | — | mixin initialiser |
| initMarkings | — | missing | — | tagged PDF |
| initMetadata | — | missing | — | XMP |
| initOutline | — | missing | — | the port creates the outline lazily in Document.Outline |
| initPageMarkings | — | missing | — | tagged PDF |
| initSubset | — | missing | — | font subsetting options |
| initText | — | missing | — | mixin initialiser |
| initVector | — | missing | — | mixin initialiser |
| lineAnnotation | — | missing | annot-line | |
| lineCap | Page.SetLineCap | match | vec-line-caps | |
| lineGap | — | missing | text-doc-line-gap | document-level leading; the port only has a per-call TextOptions.LineGap |
| lineJoin | Page.SetLineJoin | match | vec-line-joins | |
| lineTo | Page.LineTo | match | vec-line-stroke | |
| lineWidth | Page.SetLineWidth | match | vec-line-width | |
| linearGradient | pdfkit.LinearGradient | differs | grad-linear-fill, grad-linear-three-stops, grad-stroke, grad-stop-opacity | two-stop gradients use a bare Type 2 function where upstream always wraps in a Type 3 stitching function; no per-stop opacity |
| link | Page.AddLinkURI | differs | annot-link-uri, annot-link-two | the port omits the /F 4 (Print) annotation flag |
| list | — | missing | text-list | no list API of any kind |
| markContent | — | missing | — | tagged PDF |
| markStructureContent | — | missing | — | tagged PDF |
| miterLimit | Page.SetMiterLimit | match | vec-miter-limit | |
| moveDown | — | missing | text-move-down | no layout cursor to move |
| moveTo | Page.MoveTo | match | vec-line-stroke | |
| moveUp | — | missing | text-move-down | no layout cursor to move |
| note | Page.AddTextAnnotation | differs | annot-sticky-note | same /Subtype /Text, but the port takes a point rather than a rectangle (its rect sits 20 pt higher), omits /Border and omits /F |
| opacity | Page.SetAlpha | differs | alpha-both | as fillOpacity: no ExtGState reuse |
| openImage | pdfkit.LoadImage, LoadPNG, LoadJPEG | differs | img-png-rgb, img-jpeg | see image: the PNG embedding strategy differs |
| path | Page.SVGPath | differs | vec-svg-path, vec-svg-path-curves, vec-svg-path-arc, vec-svg-path-malformed | the port elevates SVG Q/T correctly to a cubic while upstream emits a v operator with the quadratic control point (a different curve); the port also accepts malformed path data that upstream rejects |
| pattern | pdfkit.NewTilingPattern | differs | pat-tiling-fill, pat-tiling-text-in-cell | upstream patterns are uncoloured (PaintType 2, TilingType 2) with the colour supplied at selection; the port's are coloured (PaintType 1, TilingType 1). The port leaves /Matrix at identity so the tiling phase is anchored to the page bottom-left rather than the top-left. The cell callback receives a Page with no document behind it and nil-panics on SetFont, DrawText, DrawImage and Shade. |
| polygon | Page.Polygon | match | vec-polygon | |
| quadraticCurveTo | — | missing | vec-quadratic | only cubic CurveTo |
| radialGradient | pdfkit.RadialGradient | differs | grad-radial-fill | as linearGradient: bare Type 2 function instead of a Type 3 stitching wrapper |
| rect | Page.Rect | match | vec-rect-fill | |
| rectAnnotation | — | missing | annot-rect | |
| ref | — | missing | — | indirect-object allocation is not exposed by the port |
| registerFont | pdfkit.LoadTrueType, LoadTrueTypeFile | untested | — | embedded fonts need a real font-file fixture; out of scope for this harness |
| restore | Page.Restore | match | vec-save-restore, alpha-scoped | |
| rotate | — | missing | xf-rotate, xf-rotate-origin | no rotation transform at all, with or without an origin |
| roundedRect | Page.RoundedRect | match | vec-rounded-rect | |
| save | Page.Save | match | vec-save-restore, alpha-scoped | |
| scale | Page.Scale | match | xf-scale, xf-nested | |
| strike | — | missing | annot-strike | the strike-out *annotation*; TextOptions.Strike draws a line instead |
| stroke | Page.Stroke | match | vec-line-stroke | |
| strokeColor | Page.SetStrokeColor, Page.SetStrokeCMYK, Page.SetStrokePattern | match | vec-rect-fill-stroke, vec-cmyk-colours | |
| strokeOpacity | Page.SetAlpha | differs | alpha-stroke-opacity | as fillOpacity |
| struct | — | missing | — | tagged PDF |
| switchToPage | — | missing | — | the port hands back a *Page per page, so there is no current-page cursor to switch |
Every case the harness streamed to both runners, with the exact upstream symbol and Go symbol it exercised. A deliberate, documented difference is a deviation and is counted apart from a mismatch.
| Case | Group | Upstream symbol | Go symbol | Status | Note |
|---|---|---|---|---|---|
| annot-link-uri | annotations | PDFDocument.link | Page.AddLinkURI | mismatch | |
| annot-link-two | annotations | PDFDocument.link | Page.AddLinkURI | mismatch | |
| annot-named-dest-and-link | annotations | PDFDocument.addNamedDestination + goTo | Document.AddNamedDest + Page.AddLinkToDest | mismatch | |
| annot-link-to-page | annotations | — | Page.AddLinkTo | mismatch | a direct link to another page object: upstream only has named destinations |
| annot-sticky-note | annotations | PDFDocument.note | Page.AddTextAnnotation | mismatch | upstream's note() takes a rectangle; the port's takes a point |
| annot-free-text | annotations | PDFDocument.textAnnotation | — | mismatch | a /FreeText annotation: the port has none |
| annot-highlight | annotations | PDFDocument.highlight | — | mismatch | |
| annot-underline | annotations | PDFDocument.underline | — | mismatch | |
| annot-strike | annotations | PDFDocument.strike | — | mismatch | |
| annot-line | annotations | PDFDocument.lineAnnotation | — | mismatch | |
| annot-rect | annotations | PDFDocument.rectAnnotation | — | mismatch | |
| annot-ellipse | annotations | PDFDocument.ellipseAnnotation | — | mismatch | |
| clip-rect | clipping | PDFDocument.clip | Page.Clip | mismatch | |
| clip-circle | clipping | PDFDocument.clip | Page.Clip | mismatch | |
| clip-even-odd | clipping | PDFDocument.clip#even-odd | Page.ClipEvenOdd | mismatch | |
| clip-nested | clipping | PDFDocument.clip | Page.Clip | mismatch | |
| clip-then-image | clipping | PDFDocument.clip + image | Page.Clip + DrawImage | mismatch | |
| enc-rc4-128-user-password | encryption | PDFDocument#options.userPassword | Document.SetEncryption | mismatch | 128-bit RC4 (V2/R3); content streams are not comparable once encrypted |
| enc-aes-128 | encryption | PDFDocument#options.pdfVersion=1.6 | EncryptOptions.UseAES | mismatch | |
| enc-owner-only | encryption | PDFDocument#options.ownerPassword | Document.SetEncryption | mismatch | opens without a password but keeps permission flags |
| enc-all-permissions | encryption | PDFDocument#options.permissions | EncryptOptions permission flags | mismatch | |
| form-text-field | forms | PDFDocument.formText | Page.AddTextField | mismatch | |
| form-checkbox | forms | PDFDocument.formCheckbox | Page.AddCheckbox | mismatch | |
| form-push-button | forms | PDFDocument.formPushButton | Page.AddPushButton | mismatch | |
| form-text-field-configured | forms | PDFDocument.formText#options | — | mismatch | a field with alignment, font size, border, background, required and multiline set: *FormField is opaque |
| form-mixed | forms | PDFDocument.formText/formCheckbox | Page.AddTextField/AddCheckbox | mismatch | |
| grad-linear-fill | gradients | PDFDocument.linearGradient | pdfkit.LinearGradient + NewShadingPattern | mismatch | |
| grad-linear-three-stops | gradients | PDFDocument.linearGradient | pdfkit.LinearGradient | mismatch | |
| grad-radial-fill | gradients | PDFDocument.radialGradient | pdfkit.RadialGradient | mismatch | |
| grad-stroke | gradients | PDFDocument.strokeColor#gradient | Page.SetStrokePattern | mismatch | |
| grad-one-stop | gradients | PDFDocument.linearGradient | pdfkit.LinearGradient | match | both sides must reject a gradient with fewer than two stops |
| grad-stop-opacity | gradients | PDFGradient.stop#opacity | — | mismatch | per-stop opacity: the port has no equivalent |
| grad-sh-operator | gradients | — | Page.Shade | mismatch | the port can paint a shading directly with sh; upstream always goes through a pattern |
| pat-tiling-fill | gradients | PDFDocument.pattern | pdfkit.NewTilingPattern | mismatch | |
| pat-tiling-text-in-cell | gradients | PDFDocument.pattern | pdfkit.NewTilingPattern | match | text inside a tiling cell: the port's callback page has no document behind it and panics |
| img-png-rgb | images | PDFDocument.image | Page.DrawImage | mismatch | opaque 8x8 RGB PNG from a committed fixture |
| img-png-rgba | images | PDFDocument.image | Page.DrawImage | mismatch | PNG with an alpha channel: exercises the soft mask |
| img-png-gray | images | PDFDocument.image | Page.DrawImage | mismatch | |
| img-jpeg | images | PDFDocument.image | Page.DrawImage | mismatch | JPEG passed through with DCTDecode on both sides |
| img-jpeg-non-square | images | PDFDocument.image | Page.DrawImage | mismatch | |
| img-twice-same-file | images | PDFDocument.image | Page.DrawImage | mismatch | same image drawn twice: is the XObject shared? |
| img-under-transform | images | PDFDocument.image | Page.DrawImage | mismatch | |
| img-missing-file | images | PDFDocument.image | pdfkit.LoadImage | match | both sides must fail on a nonexistent file |
| img-remote-url | images | PDFDocument.image | pdfkit.LoadImage | match | both runners must refuse a remote path |
| meta-all-fields | metadata | PDFDocument#options.info | Document.SetTitle/SetAuthor/... | mismatch | |
| meta-producer-creator | metadata | PDFDocument#options.info | Document.SetProducer/SetCreator | mismatch | |
| meta-unicode-title | metadata | PDFDocument#options.info | Document.SetTitle | mismatch | non-Latin text in metadata forces UTF-16BE string encoding |
| meta-empty-strings | metadata | PDFDocument#options.info | Document.SetTitle | mismatch | |
| outline-flat | outline | PDFOutline.addItem | Outline.Add | mismatch | |
| outline-nested | outline | PDFOutline.addItem | Bookmark.Add | mismatch | |
| outline-dest-y | outline | PDFOutline.addItem | Outline.Add | mismatch | a vertical position for the destination: upstream always uses /Fit |
| outline-expanded | outline | PDFOutline.addItem#expanded | — | mismatch | an initially open outline entry: the port has no option for it |
| page-single-a4 | pages | PDFDocument.addPage | Document.AddPage | mismatch | one empty A4 page: header, page tree and MediaBox only |
| page-letter | pages | PDFDocument.addPage | Document.AddPage | mismatch | |
| page-sizes-all | pages | PDFDocument.addPage | Document.AddPage | mismatch | every named size the port ships |
| page-custom-size | pages | PDFDocument.addPage | pdfkit.Custom | mismatch | |
| page-landscape | pages | PDFDocument.addPage#layout | PageSize.Landscape | mismatch | |
| page-margin-uniform | pages | PDFDocument.addPage#margin | — | mismatch | the port has no margin option at all |
| page-margins-per-side | pages | PDFDocument.addPage#margins | — | mismatch | the port has no per-side margins |
| page-rotate-90 | pages | — | — | match | neither side exposes the page /Rotate attribute |
| page-none | pages | PDFDocument.addPage | Document.AddPage | match | both sides must reject a document with no pages |
| page-unknown-size | pages | PDFDocument.addPage | Document.AddPage | match | both sides must reject an unknown named size |
| page-many | pages | PDFDocument.addPage | Document.AddPage | mismatch | |
| text-helvetica-basic | text | PDFDocument.text | Page.DrawText | mismatch | single line, explicit coordinates, no flow |
| text-standard-14 | text | PDFDocument.font | Page.SetFont | mismatch | all fourteen standard fonts, one line each |
| text-sizes | text | PDFDocument.fontSize | Page.SetFont | mismatch | |
| text-fill-colour | text | PDFDocument.fillColor | Page.SetFillColor | mismatch | |
| text-aligned-in-width | text | PDFDocument.text#align | Page.DrawTextAligned | mismatch | |
| text-wrapped-box | text | PDFDocument.text#width | Page.DrawTextBox | mismatch | greedy wrapping to a width; the port has no other flow |
| text-wrapped-justify | text | PDFDocument.text#align=justify | Page.DrawTextBox | mismatch | |
| text-wrapped-linegap | text | PDFDocument.text#lineGap | TextOptions.LineGap | mismatch | |
| text-underline-strike | text | PDFDocument.text#underline | TextOptions.Underline | mismatch | |
| text-no-font-set | text | PDFDocument.text | Page.DrawText | mismatch | no setFont first: upstream defaults to Helvetica, the port silently draws nothing |
| text-unknown-font | text | PDFDocument.font | pdfkit.StandardFont | match | both sides must reject a font that is not one of the standard 14 |
| text-winansi-high-bytes | text | PDFDocument.text | Page.DrawText | mismatch | Latin-1 range characters, to compare the single-byte encoding |
| text-parens-escapes | text | PDFDocument.text | Page.DrawText | mismatch | characters that must be escaped inside a literal string |
| text-char-spacing | text | PDFDocument.text#characterSpacing | — | mismatch | letter spacing: the port never emits Tc |
| text-flow-cursor | text | PDFDocument.text | — | mismatch | text with no coordinates, using the layout cursor: the port has none |
| text-move-down | text | PDFDocument.moveDown | — | mismatch | cursor movement: the port has no cursor |
| text-list | text | PDFDocument.list | — | mismatch | bulleted list: the port has no list API |
| text-doc-line-gap | text | PDFDocument.lineGap | — | mismatch | document-level line gap: the port only has a per-call option |
| xf-translate | transform | PDFDocument.translate | Page.Translate | mismatch | |
| xf-scale | transform | PDFDocument.scale | Page.Scale | mismatch | |
| xf-matrix | transform | PDFDocument.transform | Page.Transform | mismatch | |
| xf-nested | transform | PDFDocument.save/translate/scale/restore | Page.Save/Translate/Scale/Restore | mismatch | |
| xf-rotate | transform | PDFDocument.rotate | — | mismatch | the port has no rotate transform |
| xf-rotate-origin | transform | PDFDocument.rotate#origin | — | mismatch | the port has no rotate transform, let alone an origin option |
| xf-text-under-transform | transform | PDFDocument.scale + text | Page.Scale + DrawText | mismatch | |
| alpha-fill-opacity | transparency | PDFDocument.fillOpacity | Page.SetAlpha | mismatch | |
| alpha-stroke-opacity | transparency | PDFDocument.strokeOpacity | Page.SetAlpha | mismatch | |
| alpha-both | transparency | PDFDocument.opacity | Page.SetAlpha | mismatch | |
| alpha-scoped | transparency | PDFDocument.save/fillOpacity/restore | Page.Save/SetAlpha/Restore | mismatch | |
| alpha-reused-state | transparency | PDFDocument.fillOpacity | Page.SetAlpha | mismatch | the same alpha twice: is the ExtGState reused? |
| alpha-blend-multiply | transparency | — | Page.SetBlendMode | mismatch | blend modes: the port has them, upstream has no API |
| vec-line-stroke | vector | PDFDocument.moveTo/lineTo/stroke | Page.MoveTo/LineTo/Stroke | mismatch | |
| vec-rect-fill | vector | PDFDocument.rect/fill | Page.Rect/Fill | mismatch | |
| vec-rect-fill-stroke | vector | PDFDocument.fillAndStroke | Page.FillStroke | mismatch | |
| vec-rounded-rect | vector | PDFDocument.roundedRect | Page.RoundedRect | mismatch | |
| vec-circle | vector | PDFDocument.circle | Page.Circle | mismatch | four-Bezier circle approximation on both sides |
| vec-ellipse | vector | PDFDocument.ellipse | Page.Ellipse | mismatch |