summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorcirakg <cirakg@cirakg.xyz>2026-10-20 11:24:58 +0200
committercirakg <cirakg@cirakg.xyz>2026-10-20 11:24:58 +0200
commitcc2fa8917b65eeda8d222dcbd69e9342884bb545 (patch)
tree910b9852ac4c29d5cb43cd475dc2426b0de4dddb
parent0a39c695e1fc78a7c8b8af0b301040f422303953 (diff)
feat(codes): add Data Matrix (ECC 200, 10x10-144x144) and PDF417 encoders as jobs
Data Matrix: ASCII with digit pairs, Base256 or Upper Shift for UTF-8, interleaved Reed-Solomon in steps, utah placement; matches ppf.datamatrix. PDF417: pdf417gen's compaction, automatic or fixed EC level, columns fitted to the screen, pattern tables as generated JSON resources; matches pdf417gen. 2D matrices gain a row height and 8-at-a-time packing. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
-rw-r--r--resources/pdf417/codes0.json1
-rw-r--r--resources/pdf417/codes1.json1
-rw-r--r--resources/pdf417/codes2.json1
-rw-r--r--resources/pdf417/ec.json1
-rw-r--r--resources/pdf417/pdf417.xml11
-rw-r--r--resources/pdf417/text.json1
-rw-r--r--source/codes/Codes.mc118
-rw-r--r--source/codes/DataMatrix.mc398
-rw-r--r--source/codes/Pdf417.mc428
-rw-r--r--source/codes/Qr.mc11
10 files changed, 951 insertions, 20 deletions
diff --git a/resources/pdf417/codes0.json b/resources/pdf417/codes0.json
new file mode 100644
index 0000000..fa060e7
--- /dev/null
+++ b/resources/pdf417/codes0.json
@@ -0,0 +1 @@
+{"_author":"cirakg & claude ai","_summary":"PDF417 cluster 0 patterns (generated; do not edit).","v":[120256,125680,128380,120032,125560,128318,108736,119920,108640,86080,108592,86048,110016,120560,125820,109792,120440,125758,88256,109680,88160,89536,110320,120700,89312,110200,120638,89200,110140,89840,110460,89720,110398,89980,128506,119520,125304,128190,107712,119408,125244,107616,119352,84032,107568,119324,84000,107544,83984,108256,119672,125374,85184,108144,119612,85088,108088,119582,85040,108060,85728,108408,119742,85616,108348,85560,108318,85880,108478,85820,85790,107200,119152,125116,107104,119096,125086,83008,107056,119068,82976,107032,82960,82952,83648,107376,119228,83552,107320,119198,83504,107292,83480,83468,83824,107452,83768,107422,83740,83900,106848,118968,125022,82496,106800,118940,82464,106776,118926,82448,106764,82440,106758,82784,106936,119006,82736,106908,82712,106894,82700,82694,106974,82830,82240,106672,118876,82208,106648,118862,82192,106636,82184,106630,82180,82352,82328,82316,82080,118830,106572,106566,82050,117472,124280,127678,103616,117360,124220,103520,117304,124190,75840,103472,75808,104160,117624,124350,76992,104048,117564,76896,103992,76848,76824,77536,104312,117694,77424,104252,77368,77340,77688,104382,77628,77758,121536,126320,128700,121440,126264,128670,111680,121392,126236,111648,121368,126222,111632,121356,103104,117104,124092,112320,103008,117048,124062,112224,121656,126366,93248,74784,102936,117006,93216,112152,93200,75456,103280,117180,93888,75360,103224,117150,93792,112440,121758,93744,75288,93720,75632,103356,94064,75576,103326,94008,112542,93980,75708,94140,75678,94110,121184,126136,128606,111168,121136,126108,111136,121112,126094,111120,121100,111112,111108,102752,116920,123998,111456,102704,116892,91712,74272,121244,116878,91680,74256,102668,91664,111372,102662,74244,74592,102840,116958,92000,74544,102812,91952,111516,102798,91928,74508,74502,74680,102878,92088,74652,92060,74638,92046,92126,110912,121008,126044,110880,120984,126030,110864,120972,110856,120966,110852,110850,74048,102576,116828,90944,74016,102552,116814,90912,111000,121038,90896,73992,102534,90888,110982,90884,74160,102620,91056,74136,102606,91032,111054,91020,74118,91014,91100,91086,110752,120920,125998,110736,120908,110728,120902,110724,110722,73888,102488,116782,90528,73872,102476,90512,110796,102470,90504,73860,90500,73858,73944,90584,90572,90566,120876,120870,110658,102444,73800,90312,90308,90306,101056,116080,123580,100960,116024,70720,100912,115996,70688,100888,70672,70664,71360,101232,116156,71264,101176,116126,71216,101148,71192,71180,71536,101308,71480,101278,71452,71612,71582,118112,124600,127838,105024,118064,124572,104992,118040,124558,104976,118028,104968,118022,100704,115896,123486,105312,100656,115868,79424,70176,118172,115854,79392,105240,100620,79376,70152,79368,70496,100792,115934,79712,70448,118238,79664,105372,100750,79640,70412,79628,70584,100830,79800,70556,79772,70542,70622,79838,122176,126640,128860,122144,126616,128846,122128,126604,122120,126598,122116,104768,117936,124508,113472,104736,126684,124494,113440,122264,126670,113424,104712,117894,113416,122246,104706,69952,100528,115804,78656,69920,100504,115790,96064,78624,104856,117966,96032,113560,122318,100486,96016,78600,104838,96008,69890,70064,100572,78768,70040,100558,96176,78744,104910,96152,113614,70022,78726,70108,78812,70094,96220,78798,122016,126552,128814,122000,126540,121992,126534,121988,121986,104608,117848,124462,113056,104592,126574,113040,122060,117830,113032,104580,113028,104578,113026,69792,100440,115758,78240,69776,100428,95136,78224,104652,100422,95120,113100,69764,95112,78212,69762,78210,69848,100462,78296,69836,95192,78284,69830,95180,78278,69870,95214,121936,126508,121928,126502,121924,121922,104528,117804,112848,104520,117798,112840,121958,112836,104514,112834,69712,100396,78032,69704,100390,94672,78024,104550,94664,112870,69698,94660,78018,94658,78060,94700,94694,126486,121890,117782,104484,104482,69672,77928,94440,69666,77922,99680,68160,99632,68128,99608,115342,68112,99596,68104,99590,68448,99768,115422,68400,99740,68376,99726,68364,68358,68536,99806,68508,68494,68574,101696,116400,123740,101664,116376,101648,116364,101640,116358,101636,67904,99504,115292,72512,67872,116444,115278,72480,101784,116430,72464,67848,99462,72456,101766,67842,68016,99548,72624,67992,99534,72600,101838,72588,67974,68060,72668,68046,72654,118432,124760,127918,118416,124748,118408,124742,118404,118402,101536,116312,105888,101520,116300,105872,118476,116294,105864,101508,105860,101506,105858,67744,99416,72096,67728,116334,80800,72080,101580,99398,80784,105932,67716,80776,72068,67714,72066,67800,99438,72152,67788,80856,72140,67782,80844,72134,67822,72174,80878,126800,128940,126792,128934,126788,126786,118352,124716,122576,126828,124710,122568,126822,122564,118338,122562,101456,116268,105680,101448,116262,114128,105672,118374,114120,122598,101442,114116,105666,114114,67664,99372,71888,67656,99366,80336,71880,101478,97232,80328,105702,67650,97224,114150,71874,97220,67692,71916,67686,80364,71910,97260,80358,97254,126760,128918,126756,126754,118312,124694,122472,126774,122468,118306,122466,101416,116246,105576,101412,113896,105572,101410,113892,105570,113890,67624,99350,71784,101430,80104,71780,67618,96744,80100,71778,96740,80098,96738,71798,96758,126738,122420,122418,105524,113780,113778,71732,79988,96500,96498,66880,66848,98968,66832,66824,66820,66992,66968,66956,66950,67036,67022,100000,99984,115532,99976,115526,99972,99970,66720,98904,69024,100056,98892,69008,100044,69000,100038,68996,66690,68994,66776,98926,69080,100078,69068,66758,69062,66798,69102,116560,116552,116548,116546,99920,102096,116588,115494,102088,116582,102084,99906,102082,66640,68816,66632,98854,73168,68808,66628,73160,68804,66626,73156,68802,66668,68844,66662,73196,68838,73190,124840,124836,124834,116520,118632,124854,118628,116514,118626,99880,115478,101992,116534,106216,101988,99874,106212,101986,106210,66600,98838,68712,99894,72936,68708,66594,81384,72932,68706,81380,72930,66614,68726,72950,81398,128980,128978,124820,126900,124818,126898,116500,118580,116498,122740,118578,122738,99860,101940,99858,106100,101938,114420]}
diff --git a/resources/pdf417/codes1.json b/resources/pdf417/codes1.json
new file mode 100644
index 0000000..941aca3
--- /dev/null
+++ b/resources/pdf417/codes1.json
@@ -0,0 +1 @@
+{"_author":"cirakg & claude ai","_summary":"PDF417 cluster 3 patterns (generated; do not edit).","v":[128352,129720,125504,128304,129692,125472,128280,129678,125456,128268,125448,128262,125444,125792,128440,129758,120384,125744,128412,120352,125720,128398,120336,125708,120328,125702,120324,120672,125880,128478,110144,120624,125852,110112,120600,125838,110096,120588,110088,120582,110084,110432,120760,125918,89664,110384,120732,89632,110360,120718,89616,110348,89608,110342,89952,110520,120798,89904,110492,89880,110478,89868,90040,110558,90012,89998,125248,128176,129628,125216,128152,129614,125200,128140,125192,128134,125188,125186,119616,125360,128220,119584,125336,128206,119568,125324,119560,125318,119556,119554,108352,119728,125404,108320,119704,125390,108304,119692,108296,119686,108292,108290,85824,108464,119772,85792,108440,119758,85776,108428,85768,108422,85764,85936,108508,85912,108494,85900,85894,85980,85966,125088,128088,129582,125072,128076,125064,128070,125060,125058,119200,125144,128110,119184,125132,119176,125126,119172,119170,107424,119256,125166,107408,119244,107400,119238,107396,107394,83872,107480,119278,83856,107468,83848,107462,83844,83842,83928,107502,83916,83910,83950,125008,128044,125000,128038,124996,124994,118992,125036,118984,125030,118980,118978,106960,119020,106952,119014,106948,106946,82896,106988,82888,106982,82884,82882,82924,82918,124968,128022,124964,124962,118888,124982,118884,118882,106728,118902,106724,106722,82408,106742,82404,82402,124948,124946,118836,118834,106612,106610,124224,127664,129372,124192,127640,129358,124176,127628,124168,127622,124164,124162,117568,124336,127708,117536,124312,127694,117520,124300,117512,124294,117508,117506,104256,117680,124380,104224,117656,124366,104208,117644,104200,117638,104196,104194,77632,104368,117724,77600,104344,117710,77584,104332,77576,104326,77572,77744,104412,77720,104398,77708,77702,77788,77774,128672,129880,93168,128656,129868,92664,128648,129862,92412,128644,128642,124064,127576,129326,126368,124048,129902,126352,128716,127558,126344,124036,126340,124034,126338,117152,124120,127598,121760,117136,124108,121744,126412,124102,121736,117124,121732,117122,121730,103328,117208,124142,112544,103312,117196,112528,121804,117190,112520,103300,112516,103298,112514,75680,103384,117230,94112,75664,103372,94096,112588,103366,94088,75652,94084,75650,75736,103406,94168,75724,94156,75718,94150,75758,128592,129836,91640,128584,129830,91388,128580,91262,128578,123984,127532,126160,123976,127526,126152,128614,126148,123970,126146,116944,124012,121296,116936,124006,121288,126182,121284,116930,121282,102864,116972,111568,102856,116966,111560,121318,111556,102850,111554,74704,102892,92112,74696,102886,92104,111590,92100,74690,92098,74732,92140,74726,92134,128552,129814,90876,128548,90750,128546,123944,127510,126056,128566,126052,123938,126050,116840,123958,121064,116836,121060,116834,121058,102632,116854,111080,121078,111076,102626,111074,74216,102646,91112,74212,91108,74210,91106,74230,91126,128532,90494,128530,123924,126004,123922,126002,116788,120948,116786,120946,102516,110836,102514,110834,73972,90612,73970,90610,128522,123914,125978,116762,120890,102458,110714,123552,127320,129198,123536,127308,123528,127302,123524,123522,116128,123608,127342,116112,123596,116104,123590,116100,116098,101280,116184,123630,101264,116172,101256,116166,101252,101250,71584,101336,116206,71568,101324,71560,101318,71556,71554,71640,101358,71628,71622,71662,127824,129452,79352,127816,129446,79100,127812,78974,127810,123472,127276,124624,123464,127270,124616,127846,124612,123458,124610,115920,123500,118224,115912,123494,118216,124646,118212,115906,118210,100816,115948,105424,100808,115942,105416,118246,105412,100802,105410,70608,100844,79824,70600,100838,79816,105446,79812,70594,79810,70636,79852,70630,79846,129960,95728,113404,129956,95480,113278,129954,95356,95294,127784,129430,78588,128872,129974,95996,78462,128868,127778,95870,128866,123432,127254,124520,123428,126696,128886,123426,126692,124514,126690,115816,123446,117992,115812,122344,117988,115810,122340,117986,122338,100584,115830,104936,100580,113640,104932,100578,113636,104930,113634,70120,100598,78824,70116,96232,78820,70114,96228,78818,96226,70134,78838,129940,94968,113022,129938,94844,94782,127764,78206,128820,127762,95102,128818,123412,124468,123410,126580,124466,126578,115764,117876,115762,122100,117874,122098,100468,104692,100466,113140,104690,113138,69876,78324,69874,95220,78322,95218,129930,94588,94526,127754,128794,123402,124442,126522,115738,117818,121978,100410,104570,112890,69754,78074,94714,94398,123216,127148,123208,127142,123204,123202,115408,123244,115400,123238,115396,115394,99792,115436,99784,115430,99780,99778,68560,99820,68552,99814,68548,68546,68588,68582,127400,129238,72444,127396,72318,127394,123176,127126,123752,123172,123748,123170,123746,115304,123190,116456,115300,116452,115298,116450,99560,115318,101864,99556,101860,99554,101858,68072,99574,72680,68068,72676,68066,72674,68086,72694,129492,80632,105854,129490,80508,80446,127380,72062,127924,127378,80766,127922,123156,123700,123154,124788,123698,124786,115252,116340,115250,118516,116338,118514,99444,101620,99442,105972,101618,105970,67828,72180,67826,80884,72178,80882,97008,114044,96888,113982,96828,96798,129482,80252,130010,97148,80190,97086,127370,127898,128954,123146,123674,124730,126842,115226,116282,118394,122618,99386,101498,105722,114170,67706,71930,80378,96632,113854,96572,96542,80062,96702,96444,96414,96350,123048,123044,123042,115048,123062,115044,115042,99048,115062,99044,99042,67048,99062,67044,67042,67062,127188,68990,127186,123028,123316,123026,123314,114996,115572,114994,115570,98932,100084,98930,100082,66804,69108,66802,69106,129258,73084,73022,127178,127450,123018,123290,123834,114970,115514,116602,98874,99962,102138,66682,68858,73210,81272,106174,81212,81182,72894,81342,97648,114364,97592,114334,97564,97550,81084,97724,81054,97694,97464,114270,97436,97422,80990,97502,97372,97358,97326,114868,114866,98676,98674,66292,66290,123098,114842,115130,98618,99194,66170,67322,69310,73404,73374,81592,106334,81564,81550,73310,81630,97968,114524,97944,114510,97932,97926,81500,98012,81486,97998,97880,114478,97868,97862,81454,97902,97836,97830,69470,73564,73550,81752,106414,81740,81734,73518,81774,81708,81702]}
diff --git a/resources/pdf417/codes2.json b/resources/pdf417/codes2.json
new file mode 100644
index 0000000..d189aea
--- /dev/null
+++ b/resources/pdf417/codes2.json
@@ -0,0 +1 @@
+{"_author":"cirakg & claude ai","_summary":"PDF417 cluster 6 patterns (generated; do not edit).","v":[109536,120312,86976,109040,120060,86496,108792,119934,86256,108668,86136,129744,89056,110072,129736,88560,109820,129732,88312,109694,129730,88188,128464,129772,89592,128456,129766,89340,128452,89214,128450,125904,128492,125896,128486,125892,125890,120784,125932,120776,125926,120772,120770,110544,120812,110536,120806,110532,84928,108016,119548,84448,107768,119422,84208,107644,84088,107582,84028,129640,85488,108284,129636,85240,108158,129634,85116,85054,128232,129654,85756,128228,85630,128226,125416,128246,125412,125410,119784,125430,119780,119778,108520,119798,108516,108514,83424,107256,119166,83184,107132,83064,107070,83004,82974,129588,83704,107390,129586,83580,83518,128116,83838,128114,125172,125170,119284,119282,107508,107506,82672,106876,82552,106814,82492,82462,129562,82812,82750,128058,125050,119034,82296,106686,82236,82206,82366,82108,82078,76736,103920,117500,76256,103672,117374,76016,103548,75896,103486,75836,129384,77296,104188,129380,77048,104062,129378,76924,76862,127720,129398,77564,127716,77438,127714,124392,127734,124388,124386,117736,124406,117732,117730,104424,117750,104420,104418,112096,121592,126334,92608,111856,121468,92384,111736,121406,92272,111676,92216,111646,92188,75232,103160,117118,93664,74992,103036,93424,112252,102974,93304,74812,93244,74782,93214,129332,75512,103294,129908,129330,93944,75388,129906,93820,75326,93758,127604,75646,128756,127602,94078,128754,124148,126452,124146,126450,117236,121844,117234,121842,103412,103410,91584,111344,121212,91360,111224,121150,91248,111164,91192,111134,91164,91150,74480,102780,91888,74360,102718,91768,111422,91708,74270,91678,129306,74620,129850,92028,74558,91966,127546,128634,124026,126202,116986,121338,102906,90848,110968,121022,90736,110908,90680,110878,90652,90638,74104,102590,91000,74044,90940,74014,90910,74174,91070,90480,110780,90424,110750,90396,90382,73916,90556,73886,90526,90296,110686,90268,90254,73822,90334,90204,90190,71136,101112,116094,70896,100988,70776,100926,70716,70686,129204,71416,101246,129202,71292,71230,127348,71550,127346,123636,123634,116212,116210,101364,101362,79296,105200,118140,79072,105080,118078,78960,105020,78904,104990,78876,78862,70384,100732,79600,70264,100670,79480,105278,79420,70174,79390,129178,70524,129466,79740,70462,79678,127290,127866,123514,124666,115962,118266,100858,113376,122232,126654,95424,113264,122172,95328,113208,122142,95280,113180,95256,113166,95244,78560,104824,117950,95968,78448,104764,95856,113468,104734,95800,78364,95772,78350,95758,70008,100542,78712,69948,96120,78652,69918,96060,78622,96030,70078,78782,96190,94912,113008,122044,94816,112952,122014,94768,112924,94744,112910,94732,94726,78192,104636,95088,78136,104606,95032,113054,95004,78094,94990,69820,78268,69790,95164,78238,95134,94560,112824,121950,94512,112796,94488,112782,94476,94470,78008,104542,94648,77980,94620,77966,94606,69726,78046,94686,94384,112732,94360,112718,94348,94342,77916,94428,77902,94414,94296,112686,94284,94278,77870,94318,94252,94246,68336,99708,68216,99646,68156,68126,68476,68414,127162,123258,115450,99834,72416,101752,116414,72304,101692,72248,101662,72220,72206,67960,99518,72568,67900,72508,67870,72478,68030,72638,80576,105840,118460,80480,105784,118430,80432,105756,80408,105742,80396,80390,72048,101564,80752,71992,101534,80696,71964,80668,71950,80654,67772,72124,67742,80828,72094,80798,114016,122552,126814,96832,113968,122524,96800,113944,122510,96784,113932,96776,113926,96772,80224,105656,118366,97120,80176,105628,97072,114076,105614,97048,80140,97036,80134,97030,71864,101470,80312,71836,97208,80284,71822,97180,80270,97166,67678,71902,80350,97246,96576,113840,122460,96544,113816,122446,96528,113804,96520,113798,96516,96514,80048,105564,96688,80024,105550,96664,113870,96652,80006,96646,71772,80092,71758,96732,80078,96718,96416,113752,122414,96400,113740,96392,113734,96388,96386,79960,105518,96472,79948,96460,79942,96454,71726,79982,96494,96336,113708,96328,113702,96324,96322,79916,96364,79910,96358,96296,113686,96292,96290,79894,96310,66936,99006,66876,66846,67006,68976,100028,68920,99998,68892,68878,66748,69052,66718,69022,73056,102072,116574,73008,102044,72984,102030,72972,72966,68792,99934,73144,68764,73116,68750,73102,66654,68830,73182,81216,106160,118620,81184,106136,118606,81168,106124,81160,106118,81156,81154,72880,101980,81328,72856,101966,81304,106190,81292,72838,81286,68700,72924,68686,81372,72910,81358,114336,122712,126894,114320,122700,114312,122694,114308,114306,81056,106072,118574,97696,81040,106060,97680,114380,106054,97672,81028,97668,81026,97666,72792,101934,81112,72780,97752,81100,72774,97740,81094,97734,68654,72814,81134,97774,114256,122668,114248,122662,114244,114242,80976,106028,97488,80968,106022,97480,114278,97476,80962,97474,72748,81004,72742,97516,80998,97510,114216,122646,114212,114210,80936,106006,97384,80932,97380,80930,97378,72726,80950,97398,114196,114194,80916,97332,80914,97330,66236,66206,67256,99166,67228,67214,66142,67294,69296,100188,69272,100174,69260,69254,67164,69340,67150,69326,73376,102232,116654,73360,102220,73352,102214,73348,73346,69208,100142,73432,102254,73420,69190,73414,67118,69230,73454,106320,118700,106312,118694,106308,106306,73296,102188,81616,106348,102182,81608,73284,81604,73282,81602,69164,73324,69158,81644,73318,81638,122792,126934,122788,122786,106280,118678,114536,106276,114532,106274,114530,73256,102166,81512,73252,98024,81508,73250,98020,81506,98018,69142,73270,81526,98038,122772,122770,106260,114484,106258,114482,73236,81460,73234,97908,81458,97906,122762,106250,114458,73226,81434,97850,66396,66382,67416,99246,67404,67398,66350,67438,69456,100268,69448,100262,69444,69442,67372,69484,67366,69478,102312,116694,102308,102306,69416,100246,73576,102326,73572,69410,73570,67350,69430,73590,118740,118738,102292,106420,102290,106418,69396,73524,69394,81780,73522,81778,118730,102282,106394,69386,73498,81722,66476,66470,67496,99286,67492,67490,66454,67510,100308,100306,67476,69556,67474,69554,116714]}
diff --git a/resources/pdf417/ec.json b/resources/pdf417/ec.json
new file mode 100644
index 0000000..19c1a1e
--- /dev/null
+++ b/resources/pdf417/ec.json
@@ -0,0 +1 @@
+{"_author":"cirakg & claude ai","_summary":"PDF417 EC factors per level (generated; do not edit).","v":[[27,917],[522,568,723,809],[237,308,436,284,646,653,428,379],[274,562,232,755,599,524,801,132,295,116,442,428,295,42,176,65],[361,575,922,525,176,586,640,321,536,742,677,742,687,284,193,517,273,494,263,147,593,800,571,320,803,133,231,390,685,330,63,410],[539,422,6,93,862,771,453,106,610,287,107,505,733,877,381,612,723,476,462,172,430,609,858,822,543,376,511,400,672,762,283,184,440,35,519,31,460,594,225,535,517,352,605,158,651,201,488,502,648,733,717,83,404,97,280,771,840,629,4,381,843,623,264,543],[521,310,864,547,858,580,296,379,53,779,897,444,400,925,749,415,822,93,217,208,928,244,583,620,246,148,447,631,292,908,490,704,516,258,457,907,594,723,674,292,272,96,684,432,686,606,860,569,193,219,129,186,236,287,192,775,278,173,40,379,712,463,646,776,171,491,297,763,156,732,95,270,447,90,507,48,228,821,808,898,784,663,627,378,382,262,380,602,754,336,89,614,87,432,670,616,157,374,242,726,600,269,375,898,845,454,354,130,814,587,804,34,211,330,539,297,827,865,37,517,834,315,550,86,801,4,108,539],[524,894,75,766,882,857,74,204,82,586,708,250,905,786,138,720,858,194,311,913,275,190,375,850,438,733,194,280,201,280,828,757,710,814,919,89,68,569,11,204,796,605,540,913,801,700,799,137,439,418,592,668,353,859,370,694,325,240,216,257,284,549,209,884,315,70,329,793,490,274,877,162,749,812,684,461,334,376,849,521,307,291,803,712,19,358,399,908,103,511,51,8,517,225,289,470,637,731,66,255,917,269,463,830,730,433,848,585,136,538,906,90,2,290,743,199,655,903,329,49,802,580,355,588,188,462,10,134,628,320,479,130,739,71,263,318,374,601,192,605,142,673,687,234,722,384,177,752,607,640,455,193,689,707,805,641,48,60,732,621,895,544,261,852,655,309,697,755,756,60,231,773,434,421,726,528,503,118,49,795,32,144,500,238,836,394,280,566,319,9,647,550,73,914,342,126,32,681,331,792,620,60,609,441,180,791,893,754,605,383,228,749,760,213,54,297,134,54,834,299,922,191,910,532,609,829,189,20,167,29,872,449,83,402,41,656,505,579,481,173,404,251,688,95,497,555,642,543,307,159,924,558,648,55,497,10],[352,77,373,504,35,599,428,207,409,574,118,498,285,380,350,492,197,265,920,155,914,299,229,643,294,871,306,88,87,193,352,781,846,75,327,520,435,543,203,666,249,346,781,621,640,268,794,534,539,781,408,390,644,102,476,499,290,632,545,37,858,916,552,41,542,289,122,272,383,800,485,98,752,472,761,107,784,860,658,741,290,204,681,407,855,85,99,62,482,180,20,297,451,593,913,142,808,684,287,536,561,76,653,899,729,567,744,390,513,192,516,258,240,518,794,395,768,848,51,610,384,168,190,826,328,596,786,303,570,381,415,641,156,237,151,429,531,207,676,710,89,168,304,402,40,708,575,162,864,229,65,861,841,512,164,477,221,92,358,785,288,357,850,836,827,736,707,94,8,494,114,521,2,499,851,543,152,729,771,95,248,361,578,323,856,797,289,51,684,466,533,820,669,45,902,452,167,342,244,173,35,463,651,51,699,591,452,578,37,124,298,332,552,43,427,119,662,777,475,850,764,364,578,911,283,711,472,420,245,288,594,394,511,327,589,777,699,688,43,408,842,383,721,521,560,644,714,559,62,145,873,663,713,159,672,729,624,59,193,417,158,209,563,564,343,693,109,608,563,365,181,772,677,310,248,353,708,410,579,870,617,841,632,860,289,536,35,777,618,586,424,833,77,597,346,269,757,632,695,751,331,247,184,45,787,680,18,66,407,369,54,492,228,613,830,922,437,519,644,905,789,420,305,441,207,300,892,827,141,537,381,662,513,56,252,341,242,797,838,837,720,224,307,631,61,87,560,310,756,665,397,808,851,309,473,795,378,31,647,915,459,806,590,731,425,216,548,249,321,881,699,535,673,782,210,815,905,303,843,922,281,73,469,791,660,162,498,308,155,422,907,817,187,62,16,425,535,336,286,437,375,273,610,296,183,923,116,667,751,353,62,366,691,379,687,842,37,357,720,742,330,5,39,923,311,424,242,749,321,54,669,316,342,299,534,105,667,488,640,672,576,540,316,486,721,610,46,656,447,171,616,464,190,531,297,321,762,752,533,175,134,14,381,433,717,45,111,20,596,284,736,138,646,411,877,669,141,919,45,780,407,164,332,899,165,726,600,325,498,655,357,752,768,223,849,647,63,310,863,251,366,304,282,738,675,410,389,244,31,121,303,263]]}
diff --git a/resources/pdf417/pdf417.xml b/resources/pdf417/pdf417.xml
new file mode 100644
index 0000000..e3fca0a
--- /dev/null
+++ b/resources/pdf417/pdf417.xml
@@ -0,0 +1,11 @@
+<!--
+ Author: cirakg & claude ai
+ Summary: The PDF417 tables as JSON resources, loaded only while a PDF417 code is encoded (generated).
+-->
+<resources>
+ <jsonData id="Pdf417Codes0" filename="codes0.json" />
+ <jsonData id="Pdf417Codes1" filename="codes1.json" />
+ <jsonData id="Pdf417Codes2" filename="codes2.json" />
+ <jsonData id="Pdf417Ec" filename="ec.json" />
+ <jsonData id="Pdf417Text" filename="text.json" />
+</resources>
diff --git a/resources/pdf417/text.json b/resources/pdf417/text.json
new file mode 100644
index 0000000..e89d01b
--- /dev/null
+++ b/resources/pdf417/text.json
@@ -0,0 +1 @@
+{"_author":"cirakg & claude ai","_summary":"PDF417 text submode values (generated; do not edit).","v":[[-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,26,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1],[-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,26,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1],[-1,-1,-1,-1,-1,-1,-1,-1,-1,12,-1,-1,-1,11,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,26,-1,-1,15,18,21,10,-1,-1,-1,22,20,13,16,17,19,0,1,2,3,4,5,6,7,8,9,14,-1,-1,23,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,24,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1],[-1,-1,-1,-1,-1,-1,-1,-1,-1,12,15,-1,-1,11,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,10,20,-1,18,-1,-1,28,23,24,22,-1,13,16,17,19,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,14,0,1,-1,2,25,3,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,4,5,6,-1,7,8,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,26,21,27,9]]}
diff --git a/source/codes/Codes.mc b/source/codes/Codes.mc
index 2d307ef..bb1a4ed 100644
--- a/source/codes/Codes.mc
+++ b/source/codes/Codes.mc
@@ -53,12 +53,15 @@ module Codes {
}
}
- // A 2D code ready to draw: width x height modules, row-major, 1 = dark,
- // plus the quiet zone its symbology needs (QR 4, Data Matrix 1, ...).
+ // A 2D code ready to draw: width x height stored rows of modules,
+ // row-major, 1 = dark; each stored row is rowHeight modules high (PDF417
+ // rows are 3); plus the quiet zone its symbology needs (QR 4, Data
+ // Matrix 1, PDF417 2).
class Matrix {
var width as Number;
var height as Number;
var quiet as Number;
+ var rowHeight as Number = 1;
var modules as ByteArray;
function initialize(w as Number, h as Number, quietModules as Number, bits as ByteArray) {
@@ -68,35 +71,116 @@ module Codes {
modules = bits;
}
- // Compact form for the encrypted cache: width, height, quiet (one byte
- // each), then the modules packed 8 per byte.
+ // Height in modules as drawn.
+ function drawnHeight() as Number {
+ return height * rowHeight;
+ }
+
+ // Compact form for the encrypted cache: width, height, quiet zone,
+ // row height (one byte each), then the modules packed 8 per byte
+ // (eight per loop round: this runs in one event, under the watchdog).
function toBytes() as ByteArray {
- var out = [width, height, quiet]b;
- var packed = new [(modules.size() + 7) / 8]b;
- for (var i = 0; i < modules.size(); i++) {
- if (modules[i] == 1) {
- packed[i / 8] = packed[i / 8] | (0x80 >> (i % 8));
- }
+ var m = modules;
+ var n = m.size();
+ var full = n / 8;
+ var out = new [4 + (n + 7) / 8]b;
+ out[0] = width;
+ out[1] = height;
+ out[2] = quiet;
+ out[3] = rowHeight;
+ for (var i = 0; i < full; i++) {
+ var k = i * 8;
+ out[4 + i] = (m[k] << 7) | (m[k + 1] << 6) | (m[k + 2] << 5) | (m[k + 3] << 4)
+ | (m[k + 4] << 3) | (m[k + 5] << 2) | (m[k + 6] << 1) | m[k + 7];
+ }
+ for (var k = full * 8; k < n; k++) {
+ out[4 + full] = out[4 + full] | (m[k] << (7 - k % 8));
}
- out.addAll(packed);
return out;
}
static function fromBytes(data as ByteArray) as Matrix? {
- if (data.size() < 3) {
+ if (data.size() < 4) {
return null;
}
var w = data[0];
var h = data[1];
- if (w == 0 || h == 0 || data.size() != 3 + (w * h + 7) / 8) {
+ var n = w * h;
+ if (w == 0 || h == 0 || data[3] == 0 || data.size() != 4 + (n + 7) / 8) {
return null;
}
- var bits = new [w * h]b;
- for (var i = 0; i < w * h; i++) {
- bits[i] = (data[3 + i / 8] >> (7 - i % 8)) & 1;
+ var bits = new [(n + 7) / 8 * 8]b;
+ for (var i = 0; i < (n + 7) / 8; i++) {
+ var v = data[4 + i];
+ var k = i * 8;
+ bits[k] = (v >> 7) & 1;
+ bits[k + 1] = (v >> 6) & 1;
+ bits[k + 2] = (v >> 5) & 1;
+ bits[k + 3] = (v >> 4) & 1;
+ bits[k + 4] = (v >> 3) & 1;
+ bits[k + 5] = (v >> 2) & 1;
+ bits[k + 6] = (v >> 1) & 1;
+ bits[k + 7] = v & 1;
}
- return new Matrix(w, h, data[2], bits);
+ var matrix = new Matrix(w, h, data[2], bits.size() == n ? bits : bits.slice(0, n));
+ matrix.rowHeight = data[3];
+ return matrix;
+ }
+ }
+
+ // A 2D encoder run as a resumable job (watchdog): when step() returns
+ // true, either error is set or matrix() is the code.
+ class MatrixJob extends Jobs.Job {
+ var error as String? = null;
+
+ function initialize() {
+ Job.initialize();
+ }
+
+ // Progress 0-100 for the UI.
+ function percent() as Number {
+ return 0;
+ }
+
+ function matrix() as Matrix? {
+ return null;
+ }
+ }
+
+ // The job that encodes a 2D symbology, or null for linear ones. PDF417
+ // chooses its columns for the screen, so it gets the screen's size.
+ function matrixJob(symbology as Number, payload as String, options as Number, screenW as Number, screenH as Number,
+ round as Boolean) as MatrixJob? {
+ if (symbology == QR) {
+ return new Qr.QrJob(payload, ecLevel(options), 0, -1);
+ }
+ if (symbology == DATAMATRIX) {
+ return new DataMatrix.DataMatrixJob(payload);
+ }
+ if (symbology == PDF417) {
+ return new Pdf417.Pdf417Job(payload, ecLevel(options), screenW, screenH, round);
+ }
+ return null;
+ }
+
+ // Smallest module size allowed (README 12.2): 1 px for linear codes and
+ // PDF417 (a stacked linear code), 2 px for the square 2D codes.
+ function minModulePx(symbology as Number) as Number {
+ return isLinear(symbology) || symbology == PDF417 ? 1 : 2;
+ }
+
+ // True when a code of w x h modules (quiet zone included) at px pixels
+ // per module fits the screen: inside the circle (less a 4 px margin) on
+ // round screens; elsewhere the full width and the height less room for
+ // the name and ID lines.
+ function fits(w as Number, h as Number, px as Number, screenW as Number, screenH as Number, round as Boolean) as Boolean {
+ var width = w * px;
+ var height = h * px;
+ if (round) {
+ var r = screenW / 2 - 4;
+ return width.toLong() * width + height.toLong() * height <= 4l * r * r;
}
+ return width <= screenW - 4 && height <= screenH * 76 / 100;
}
function isMatrix(symbology as Number) as Boolean {
diff --git a/source/codes/DataMatrix.mc b/source/codes/DataMatrix.mc
new file mode 100644
index 0000000..45d5ff0
--- /dev/null
+++ b/source/codes/DataMatrix.mc
@@ -0,0 +1,398 @@
+// Author: cirakg & claude ai
+// Summary: Data Matrix ECC 200 encoder (ISO/IEC 16022): square sizes 10x10 to 144x144, ASCII and Base256, as a resumable job.
+//
+// Steps:
+// 1. Encode the UTF-8 bytes: ASCII (digit pairs as one codeword) when
+// every byte is below 128, otherwise the cheaper of ASCII with Upper
+// Shift per byte and one Base256 field (randomised, ISO 16022 annex B).
+// C40, Text, X12 and EDIFACT are deferred (README 12.1).
+// 2. Pick the smallest square size, pad (129, then randomised pads).
+// 3. Reed-Solomon over GF(256) (x^8+x^5+x^3+x^2+1, roots a^1..a^n),
+// per interleaved block, in chunks.
+// 4. Place the codewords with the standard "utah" placement, a few
+// diagonal sweeps per step, then add the finder and clock patterns of
+// every data region.
+// Verified against ppf.datamatrix (ASCII mode) and by decoding with zxing-cpp.
+
+import Toybox.Lang;
+
+module DataMatrix {
+
+ // Square sizes: [symbol size, data region size, regions per side,
+ // data codewords, EC codewords, interleaved blocks] (ISO 16022 table 7).
+ const SIZES = [
+ [10, 8, 1, 3, 5, 1], [12, 10, 1, 5, 7, 1], [14, 12, 1, 8, 10, 1], [16, 14, 1, 12, 12, 1],
+ [18, 16, 1, 18, 14, 1], [20, 18, 1, 22, 18, 1], [22, 20, 1, 30, 20, 1], [24, 22, 1, 36, 24, 1],
+ [26, 24, 1, 44, 28, 1], [32, 14, 2, 62, 36, 1], [36, 16, 2, 86, 42, 1], [40, 18, 2, 114, 48, 1],
+ [44, 20, 2, 144, 56, 1], [48, 22, 2, 174, 68, 1], [52, 24, 2, 204, 84, 2], [64, 14, 4, 280, 112, 2],
+ [72, 16, 4, 368, 144, 4], [80, 18, 4, 456, 192, 4], [88, 20, 4, 576, 224, 4], [96, 22, 4, 696, 272, 4],
+ [104, 24, 4, 816, 336, 6], [120, 18, 6, 1050, 408, 6], [132, 20, 6, 1304, 496, 8], [144, 22, 6, 1558, 620, 10]
+ ];
+ const QUIET = 1;
+ const PAD = 129;
+ const UPPER_SHIFT = 235;
+ const BASE256_LATCH = 231;
+ // Work per step, sized for the 120,000-bytecode watchdog.
+ const RS_SYMBOLS_PER_STEP = 40;
+ const SWEEPS_PER_STEP = 6;
+
+ var field as ReedSolomon.Field? = null;
+
+ function theField() as ReedSolomon.Field {
+ var f = field;
+ if (f == null) {
+ f = new ReedSolomon.Field(0x12D, 256, 1);
+ field = f;
+ }
+ return f;
+ }
+
+ // Codewords for data: ASCII, or Base256 when cheaper (non-ASCII text).
+ function codewords(data as ByteArray) as Array<Number> {
+ var ascii = [] as Array<Number>;
+ var highBytes = 0;
+ var i = 0;
+ while (i < data.size()) {
+ var b = data[i];
+ if (b >= 0x30 && b <= 0x39 && i + 1 < data.size() && data[i + 1] >= 0x30 && data[i + 1] <= 0x39) {
+ ascii.add(130 + (b - 0x30) * 10 + data[i + 1] - 0x30);
+ i += 2;
+ continue;
+ }
+ if (b >= 128) {
+ ascii.add(UPPER_SHIFT);
+ ascii.add(b - 128 + 1);
+ highBytes++;
+ } else {
+ ascii.add(b + 1);
+ }
+ i++;
+ }
+ var n = data.size();
+ var base256Cost = 1 + (n <= 249 ? 1 : 2) + n;
+ if (highBytes == 0 || ascii.size() <= base256Cost) {
+ return ascii;
+ }
+ var out = [BASE256_LATCH] as Array<Number>;
+ if (n <= 249) {
+ out.add(randomise255(n, 2));
+ } else {
+ out.add(randomise255(n / 250 + 249, 2));
+ out.add(randomise255(n % 250, 3));
+ }
+ for (var k = 0; k < n; k++) {
+ out.add(randomise255(data[k], out.size() + 1));
+ }
+ return out;
+ }
+
+ // The 255-state randomising of Base256 values at 1-based position pos.
+ function randomise255(value as Number, pos as Number) as Number {
+ var v = value + ((149 * pos) % 255) + 1;
+ return v <= 255 ? v : v - 256;
+ }
+
+ // The 253-state randomised pad at 1-based position pos.
+ function pad253(pos as Number) as Number {
+ var v = PAD + ((149 * pos) % 253) + 1;
+ return v <= 254 ? v : v - 254;
+ }
+
+ // Encode synchronously (tests); the app uses DataMatrixJob.
+ function encode(text as String) as Codes.Matrix or Codes.CodeError {
+ var job = new DataMatrixJob(text);
+ Jobs.runToCompletion(job, 1);
+ var err = job.error;
+ return err != null ? new Codes.CodeError(err) : job.matrix() as Codes.Matrix;
+ }
+
+ class DataMatrixJob extends Codes.MatrixJob {
+
+ private var _data as ByteArray;
+ private var _phase as Number = 0;
+ private var _size as Array<Number> = [] as Array<Number>;
+ private var _cw as Array<Number> = [] as Array<Number>;
+ private var _blocks as Array<Array<Number>> = [] as Array<Array<Number>>;
+ private var _rem as Array<Number> = [] as Array<Number>;
+ private var _block as Number = 0;
+ private var _at as Number = 0;
+ private var _ecTotal as Array<Number> = [] as Array<Number>;
+ // Placement state.
+ private var _nrow as Number = 0;
+ private var _ncol as Number = 0;
+ private var _place as Array<Number> = [] as Array<Number>; // 10 * codeword + bit, 0 = empty, -1 = fixed dark
+ private var _chr as Number = 1;
+ private var _row as Number = 4;
+ private var _col as Number = 0;
+ private var _result as Codes.Matrix? = null;
+
+ function initialize(text as String) {
+ MatrixJob.initialize();
+ _data = Bytes.fromUtf8(text);
+ }
+
+ function percent() as Number {
+ return _phase * 25;
+ }
+
+ function matrix() as Codes.Matrix? {
+ return _result;
+ }
+
+ function step(budget as Number) as Boolean {
+ if (_phase == 0) {
+ prepare();
+ } else if (_phase == 1) {
+ eccStep();
+ } else if (_phase == 2) {
+ placeStep();
+ } else if (_phase == 3) {
+ build();
+ }
+ return _phase >= 4 || error != null;
+ }
+
+ // Codewords, size, padding, blocks.
+ private function prepare() as Void {
+ if (_data.size() == 0) {
+ error = "Data Matrix needs some text";
+ return;
+ }
+ var cw = codewords(_data);
+ for (var i = 0; i < SIZES.size() && _size.size() == 0; i++) {
+ var s = SIZES[i] as Array<Number>;
+ if (cw.size() <= s[3]) {
+ _size = s;
+ }
+ }
+ if (_size.size() == 0) {
+ error = "too long for Data Matrix (" + cw.size() + " codewords, at most 1558)";
+ return;
+ }
+ var capacity = _size[3];
+ if (cw.size() < capacity) {
+ cw.add(PAD);
+ while (cw.size() < capacity) {
+ cw.add(pad253(cw.size() + 1));
+ }
+ }
+ _cw = cw;
+ var blocks = _size[5];
+ for (var b = 0; b < blocks; b++) {
+ var block = [] as Array<Number>;
+ for (var k = b; k < capacity; k += blocks) {
+ block.add(cw[k]);
+ }
+ _blocks.add(block);
+ }
+ _ecTotal = new [_size[4]] as Array<Number>;
+ startBlock();
+ _phase = 1;
+ }
+
+ private function startBlock() as Void {
+ var n = _size[4] / _size[5];
+ _rem = new [n] as Array<Number>;
+ for (var k = 0; k < n; k++) {
+ _rem[k] = 0;
+ }
+ _at = 0;
+ }
+
+ // Reed-Solomon, RS_SYMBOLS_PER_STEP data symbols per step.
+ private function eccStep() as Void {
+ var f = theField();
+ var data = _blocks[_block];
+ var n = _rem.size();
+ var g = f.generator(n);
+ var end = _at + RS_SYMBOLS_PER_STEP < data.size() ? _at + RS_SYMBOLS_PER_STEP : data.size();
+ for (var i = _at; i < end; i++) {
+ var factor = data[i] ^ _rem[0];
+ for (var k = 0; k < n - 1; k++) {
+ _rem[k] = _rem[k + 1] ^ f.multiply(g[k], factor);
+ }
+ _rem[n - 1] = f.multiply(g[n - 1], factor);
+ }
+ _at = end;
+ if (_at < data.size()) {
+ return;
+ }
+ // Interleave this block's EC codewords.
+ var blocks = _size[5];
+ for (var k = 0; k < n; k++) {
+ _ecTotal[_block + k * blocks] = _rem[k];
+ }
+ _block++;
+ if (_block < blocks) {
+ startBlock();
+ return;
+ }
+ _cw.addAll(_ecTotal);
+ _blocks = [] as Array<Array<Number>>;
+ _nrow = _size[1] * _size[2];
+ _ncol = _nrow;
+ _place = new [_nrow * _ncol] as Array<Number>;
+ for (var k = 0; k < _place.size(); k++) {
+ _place[k] = 0;
+ }
+ _phase = 2;
+ }
+
+ // ISO 16022 annex F placement, SWEEPS_PER_STEP diagonal sweeps per step.
+ private function placeStep() as Void {
+ var nrow = _nrow;
+ var ncol = _ncol;
+ for (var s = 0; s < SWEEPS_PER_STEP && (_row < nrow || _col < ncol); s++) {
+ if (_row == nrow && _col == 0) {
+ corner1();
+ }
+ if (_row == nrow - 2 && _col == 0 && ncol % 4 != 0) {
+ corner2();
+ }
+ if (_row == nrow - 2 && _col == 0 && ncol % 8 == 4) {
+ corner3();
+ }
+ if (_row == nrow + 4 && _col == 2 && ncol % 8 == 0) {
+ corner4();
+ }
+ do {
+ if (_row < nrow && _col >= 0 && _place[_row * ncol + _col] == 0) {
+ utah(_row, _col);
+ }
+ _row -= 2;
+ _col += 2;
+ } while (_row >= 0 && _col < ncol);
+ _row += 1;
+ _col += 3;
+ do {
+ if (_row >= 0 && _col < ncol && _place[_row * ncol + _col] == 0) {
+ utah(_row, _col);
+ }
+ _row += 2;
+ _col -= 2;
+ } while (_row < nrow && _col >= 0);
+ _row += 3;
+ _col += 1;
+ }
+ if (_row < nrow || _col < ncol) {
+ return;
+ }
+ if (_place[nrow * ncol - 1] == 0) {
+ _place[nrow * ncol - 1] = -1;
+ _place[nrow * ncol - ncol - 2] = -1;
+ }
+ _phase = 3;
+ }
+
+ private function setModule(row as Number, col as Number, bit as Number) as Void {
+ var r = row;
+ var c = col;
+ if (r < 0) {
+ r += _nrow;
+ c += 4 - ((_nrow + 4) % 8);
+ }
+ if (c < 0) {
+ c += _ncol;
+ r += 4 - ((_ncol + 4) % 8);
+ }
+ _place[r * _ncol + c] = 10 * _chr + bit;
+ }
+
+ private function utah(row as Number, col as Number) as Void {
+ setModule(row - 2, col - 2, 1);
+ setModule(row - 2, col - 1, 2);
+ setModule(row - 1, col - 2, 3);
+ setModule(row - 1, col - 1, 4);
+ setModule(row - 1, col, 5);
+ setModule(row, col - 2, 6);
+ setModule(row, col - 1, 7);
+ setModule(row, col, 8);
+ _chr++;
+ }
+
+ private function corner1() as Void {
+ setModule(_nrow - 1, 0, 1);
+ setModule(_nrow - 1, 1, 2);
+ setModule(_nrow - 1, 2, 3);
+ setModule(0, _ncol - 2, 4);
+ setModule(0, _ncol - 1, 5);
+ setModule(1, _ncol - 1, 6);
+ setModule(2, _ncol - 1, 7);
+ setModule(3, _ncol - 1, 8);
+ _chr++;
+ }
+
+ private function corner2() as Void {
+ setModule(_nrow - 3, 0, 1);
+ setModule(_nrow - 2, 0, 2);
+ setModule(_nrow - 1, 0, 3);
+ setModule(0, _ncol - 4, 4);
+ setModule(0, _ncol - 3, 5);
+ setModule(0, _ncol - 2, 6);
+ setModule(0, _ncol - 1, 7);
+ setModule(1, _ncol - 1, 8);
+ _chr++;
+ }
+
+ private function corner3() as Void {
+ setModule(_nrow - 3, 0, 1);
+ setModule(_nrow - 2, 0, 2);
+ setModule(_nrow - 1, 0, 3);
+ setModule(0, _ncol - 2, 4);
+ setModule(0, _ncol - 1, 5);
+ setModule(1, _ncol - 1, 6);
+ setModule(2, _ncol - 1, 7);
+ setModule(3, _ncol - 1, 8);
+ _chr++;
+ }
+
+ private function corner4() as Void {
+ setModule(_nrow - 1, 0, 1);
+ setModule(_nrow - 1, _ncol - 1, 2);
+ setModule(0, _ncol - 3, 3);
+ setModule(0, _ncol - 2, 4);
+ setModule(0, _ncol - 1, 5);
+ setModule(1, _ncol - 3, 6);
+ setModule(1, _ncol - 2, 7);
+ setModule(1, _ncol - 1, 8);
+ _chr++;
+ }
+
+ // The symbol: data modules mapped into the regions, plus each
+ // region's finder (solid left and bottom) and clock (top, right).
+ private function build() as Void {
+ var size = _size[0];
+ var region = _size[1];
+ var box = region + 2;
+ var m = new [size * size]b;
+ for (var y = 0; y < size; y++) {
+ var ry = y % box;
+ for (var x = 0; x < size; x++) {
+ var rx = x % box;
+ var dark = 0;
+ if (rx == 0 || ry == box - 1) {
+ dark = 1;
+ } else if (ry == 0) {
+ dark = rx % 2 == 0 ? 1 : 0;
+ } else if (rx == box - 1) {
+ dark = ry % 2 == 1 ? 1 : 0;
+ } else {
+ var mr = (y / box) * region + ry - 1;
+ var mc = (x / box) * region + rx - 1;
+ var v = _place[mr * _ncol + mc];
+ if (v == -1) {
+ dark = 1;
+ } else if (v > 0) {
+ var cw = _cw[v / 10 - 1];
+ dark = (cw >> (8 - v % 10)) & 1;
+ }
+ }
+ m[y * size + x] = dark;
+ }
+ }
+ _place = [] as Array<Number>;
+ _result = new Codes.Matrix(size, size, QUIET, m);
+ _phase = 4;
+ }
+ }
+}
diff --git a/source/codes/Pdf417.mc b/source/codes/Pdf417.mc
new file mode 100644
index 0000000..b17b5df
--- /dev/null
+++ b/source/codes/Pdf417.mc
@@ -0,0 +1,428 @@
+// Author: cirakg & claude ai
+// Summary: PDF417 encoder (ISO/IEC 15438): text, byte and numeric compaction as pdf417gen does it, columns fitted to the screen.
+//
+// Steps:
+// 1. Compaction, mirroring pdf417gen (the test reference): the bytes are
+// split into runs of digits, text-compactable characters and other
+// bytes; digit runs under 13 next to text become text; equal neighbours
+// merge. Text uses the four submodes (switch codes, pairs of values,
+// 29 as padding), numeric packs 44 digits per base-900 group, byte
+// packs 6 bytes into 5 codewords.
+// 2. Error-correction level: the option's fixed level, or automatic
+// (ISO 15438 annex E: 2 up to 40 data codewords, then 3, 4, 5).
+// 3. Columns and module size: the largest module size, then the fewest
+// columns, for which the symbol (with a 2-module quiet zone and rows 3
+// modules high) fits the screen (Codes.fits).
+// 4. Padding (900), length descriptor, Reed-Solomon mod 929 in steps.
+// 5. Rows: start, left row indicator, data, right row indicator, stop,
+// each codeword drawn from its cluster's 17-module pattern.
+// The 2,787 patterns, EC factors and text table are JSON resources
+// generated from pdf417gen (tools/gen_pdf417_tables.py), loaded only while
+// encoding.
+
+import Toybox.Lang;
+import Toybox.Math;
+import Toybox.WatchUi;
+
+module Pdf417 {
+
+ const START = 0x1fea8;
+ const STOP = 0x3fa29;
+ const PAD = 900;
+ const TEXT_LATCH = 900;
+ const BYTE_LATCH = 901;
+ const BYTE_LATCH_6 = 924;
+ const NUMERIC_LATCH = 902;
+ const ROW_HEIGHT = 3;
+ const QUIET = 2;
+ const MAX_CODEWORDS = 928;
+
+ const KIND_TEXT = 0;
+ const KIND_BYTE = 1;
+ const KIND_NUMERIC = 2;
+
+ // Text submodes and the switch codes between them [from][to].
+ const UPPER = 0;
+ const LOWER = 1;
+ const MIXED = 2;
+ const PUNCT = 3;
+ const SWITCHES = [
+ [[], [27], [28], [28, 25]],
+ [[28, 28], [], [28], [28, 25]],
+ [[28], [27], [], [25]],
+ [[29], [29, 27], [29, 28], []]
+ ];
+ const PREFERENCE = [1, 0, 2, 3]; // lower, upper, mixed, punctuation
+ const EC_STEP_WORDS = 20;
+
+ // One of the generated table resources ("v" holds the values).
+ function table(id as ResourceId) as Array {
+ return (WatchUi.loadResource(id) as Dictionary)["v"] as Array;
+ }
+
+ // Data codewords (without length descriptor and padding).
+ function dataCodewords(data as ByteArray, text as Array<Array<Number>>) as Array<Number> {
+ // Runs of one kind: [kind, start, end) triples.
+ var runs = [] as Array<Number>;
+ for (var i = 0; i < data.size(); i++) {
+ var kind = kindOf(data[i], text);
+ var n = runs.size();
+ if (n > 0 && runs[n - 3] == kind) {
+ runs[n - 1] = i + 1;
+ } else {
+ runs.add(kind);
+ runs.add(i);
+ runs.add(i + 1);
+ }
+ }
+ // Short digit runs next to text become text.
+ for (var r = 0; r < runs.size(); r += 3) {
+ if (runs[r] == KIND_NUMERIC && runs[r + 2] - runs[r + 1] < 13
+ && ((r > 0 && runs[r - 3] == KIND_TEXT) || (r + 3 < runs.size() && runs[r + 3] == KIND_TEXT))) {
+ runs[r] = -KIND_TEXT - 10; // marked; resolved below so later checks see the original kinds
+ }
+ }
+ for (var r = 0; r < runs.size(); r += 3) {
+ if (runs[r] == -KIND_TEXT - 10) {
+ runs[r] = KIND_TEXT;
+ }
+ }
+ // Merge equal neighbours.
+ var merged = [] as Array<Number>;
+ for (var r = 0; r < runs.size(); r += 3) {
+ var n = merged.size();
+ if (n > 0 && merged[n - 3] == runs[r]) {
+ merged[n - 1] = runs[r + 2];
+ } else {
+ merged.add(runs[r]);
+ merged.add(runs[r + 1]);
+ merged.add(runs[r + 2]);
+ }
+ }
+ var out = [] as Array<Number>;
+ for (var r = 0; r < merged.size(); r += 3) {
+ var kind = merged[r];
+ var start = merged[r + 1];
+ var end = merged[r + 2];
+ if (r > 0 || kind != KIND_TEXT) {
+ out.add(kind == KIND_TEXT ? TEXT_LATCH : (kind == KIND_NUMERIC ? NUMERIC_LATCH
+ : ((end - start) % 6 == 0 ? BYTE_LATCH_6 : BYTE_LATCH)));
+ }
+ if (kind == KIND_TEXT) {
+ compactText(data, start, end, text, out);
+ } else if (kind == KIND_NUMERIC) {
+ compactNumeric(data, start, end, out);
+ } else {
+ compactBytes(data, start, end, out);
+ }
+ }
+ return out;
+ }
+
+ function kindOf(b as Number, text as Array<Array<Number>>) as Number {
+ if (b >= 0x30 && b <= 0x39) {
+ return KIND_NUMERIC;
+ }
+ if (b < 127) {
+ for (var m = 0; m < 4; m++) {
+ if ((text[m][b] as Number) >= 0) {
+ return KIND_TEXT;
+ }
+ }
+ }
+ return KIND_BYTE;
+ }
+
+ function compactText(data as ByteArray, start as Number, end as Number, text as Array<Array<Number>>, out as Array<Number>) as Void {
+ var interim = [] as Array<Number>;
+ var submode = UPPER;
+ for (var i = start; i < end; i++) {
+ var c = data[i];
+ if ((text[submode][c] as Number) < 0) {
+ var next = submode;
+ for (var p = 0; p < 4; p++) {
+ var m = PREFERENCE[p] as Number;
+ if ((text[m][c] as Number) >= 0) {
+ next = m;
+ break;
+ }
+ }
+ interim.addAll((SWITCHES[submode] as Array)[next] as Array<Number>);
+ submode = next;
+ }
+ interim.add(text[submode][c] as Number);
+ }
+ for (var i = 0; i < interim.size(); i += 2) {
+ out.add(30 * interim[i] + (i + 1 < interim.size() ? interim[i + 1] : 29));
+ }
+ }
+
+ // 44 digits per group: the value of "1" followed by the digits, in base 900.
+ function compactNumeric(data as ByteArray, start as Number, end as Number, out as Array<Number>) as Void {
+ for (var g = start; g < end; g += 44) {
+ var stop = g + 44 < end ? g + 44 : end;
+ var decimal = [1] as Array<Number>;
+ for (var i = g; i < stop; i++) {
+ decimal.add(data[i] - 0x30);
+ }
+ var base900 = [] as Array<Number>;
+ while (decimal.size() > 0) {
+ // Divide the decimal number by 900, keeping the quotient.
+ var quotient = [] as Array<Number>;
+ var rem = 0;
+ for (var k = 0; k < decimal.size(); k++) {
+ var cur = rem * 10 + decimal[k];
+ var q = cur / 900;
+ rem = cur % 900;
+ if (quotient.size() > 0 || q > 0) {
+ quotient.add(q);
+ }
+ }
+ base900.add(rem);
+ decimal = quotient;
+ }
+ for (var k = base900.size() - 1; k >= 0; k--) {
+ out.add(base900[k]);
+ }
+ }
+ }
+
+ // 6 bytes per group as 5 base-900 codewords; a shorter last group as is.
+ function compactBytes(data as ByteArray, start as Number, end as Number, out as Array<Number>) as Void {
+ for (var g = start; g < end; g += 6) {
+ if (g + 6 > end) {
+ for (var i = g; i < end; i++) {
+ out.add(data[i]);
+ }
+ return;
+ }
+ var value = 0l;
+ for (var i = g; i < g + 6; i++) {
+ value = value * 256 + data[i];
+ }
+ var digits = new [5] as Array<Number>;
+ for (var k = 4; k >= 0; k--) {
+ digits[k] = (value % 900).toNumber();
+ value = value / 900;
+ }
+ out.addAll(digits);
+ }
+ }
+
+ // ISO 15438 annex E recommended level for the data codewords.
+ function autoLevel(dataCount as Number) as Number {
+ if (dataCount <= 40) {
+ return 2;
+ }
+ if (dataCount <= 160) {
+ return 3;
+ }
+ return dataCount <= 320 ? 4 : 5;
+ }
+
+ // The option's level: Low 2, Medium automatic, Quartile 5, High 6.
+ function levelFor(option as Number, dataCount as Number) as Number {
+ return [2, autoLevel(dataCount), 5, 6][option] as Number;
+ }
+
+ function leftIndicator(row as Number, rows as Number, cols as Number, level as Number) as Number {
+ var t = row % 3;
+ var x = t == 0 ? (rows - 1) / 3 : (t == 1 ? level * 3 + (rows - 1) % 3 : cols - 1);
+ return 30 * (row / 3) + x;
+ }
+
+ function rightIndicator(row as Number, rows as Number, cols as Number, level as Number) as Number {
+ var t = row % 3;
+ var x = t == 0 ? cols - 1 : (t == 1 ? (rows - 1) / 3 : level * 3 + (rows - 1) % 3);
+ return 30 * (row / 3) + x;
+ }
+
+ // Encode synchronously with given columns and level (tests).
+ function encode(text as String, cols as Number, level as Number) as Codes.Matrix or Codes.CodeError {
+ var job = new Pdf417Job(text, 0, 0, 0, false);
+ job.force(cols, level);
+ Jobs.runToCompletion(job, 1);
+ var err = job.error;
+ return err != null ? new Codes.CodeError(err) : job.matrix() as Codes.Matrix;
+ }
+
+ class Pdf417Job extends Codes.MatrixJob {
+
+ var columns as Number = 0;
+ var level as Number = -1;
+ var rows as Number = 0;
+
+ private var _data as ByteArray;
+ private var _option as Number;
+ private var _screenW as Number;
+ private var _screenH as Number;
+ private var _round as Boolean;
+ private var _phase as Number = 0;
+ private var _words as Array<Number> = [] as Array<Number>;
+ private var _ec as Array<Number> = [] as Array<Number>;
+ private var _factors as Array<Number> = [] as Array<Number>;
+ private var _codes as Array<Array<Number>> = [] as Array<Array<Number>>;
+ private var _at as Number = 0;
+ private var _modules as ByteArray = []b;
+ private var _result as Codes.Matrix? = null;
+
+ // option: the EC option (0-3). The screen size decides the columns.
+ function initialize(text as String, option as Number, screenW as Number, screenH as Number, round as Boolean) {
+ MatrixJob.initialize();
+ _data = Bytes.fromUtf8(text);
+ _option = option;
+ _screenW = screenW;
+ _screenH = screenH;
+ _round = round;
+ }
+
+ // Tests: fixed columns and level instead of the screen and option.
+ function force(cols as Number, ecLevel as Number) as Void {
+ columns = cols;
+ level = ecLevel;
+ }
+
+ function percent() as Number {
+ return _phase == 1 && _words.size() > 0 ? 20 + 60 * _at / _words.size() : _phase * 30;
+ }
+
+ function matrix() as Codes.Matrix? {
+ return _result;
+ }
+
+ function step(budget as Number) as Boolean {
+ if (_phase == 0) {
+ prepare();
+ } else if (_phase == 1) {
+ ecStep();
+ } else if (_phase == 2) {
+ rowsStep();
+ }
+ return _phase >= 3 || error != null;
+ }
+
+ private function prepare() as Void {
+ if (_data.size() == 0) {
+ error = "PDF417 needs some text";
+ return;
+ }
+ var data = dataCodewords(_data, table(Rez.JsonData.Pdf417Text) as Array<Array<Number>>);
+ if (level < 0) {
+ level = levelFor(_option, data.size());
+ }
+ var ecCount = 1 << (level + 1);
+ if (columns == 0 && !chooseLayout(data.size() + ecCount + 1)) {
+ error = "too large for this screen as PDF417 (" + data.size() + " codewords)";
+ return;
+ }
+ var total = data.size() + ecCount + 1;
+ var padding = total % columns == 0 ? 0 : columns - total % columns;
+ var length = data.size() + padding + 1;
+ rows = (total + padding) / columns;
+ if (length > MAX_CODEWORDS || rows > 90) {
+ error = "too long for PDF417 (" + data.size() + " codewords)";
+ return;
+ }
+ if (rows < 3) {
+ error = "PDF417 needs at least 3 rows; use fewer columns";
+ return;
+ }
+ _words = [length] as Array<Number>;
+ _words.addAll(data);
+ for (var i = 0; i < padding; i++) {
+ _words.add(PAD);
+ }
+ _factors = (table(Rez.JsonData.Pdf417Ec) as Array<Array<Number>>)[level];
+ _codes = [table(Rez.JsonData.Pdf417Codes0) as Array<Number>, table(Rez.JsonData.Pdf417Codes1) as Array<Number>,
+ table(Rez.JsonData.Pdf417Codes2) as Array<Number>];
+ _ec = new [ecCount] as Array<Number>;
+ for (var i = 0; i < ecCount; i++) {
+ _ec[i] = 0;
+ }
+ _at = 0;
+ _phase = 1;
+ }
+
+ // The largest module size, then the fewest columns, that fit.
+ private function chooseLayout(codewords as Number) as Boolean {
+ for (var px = 6; px >= 1; px--) {
+ for (var cols = 1; cols <= 30; cols++) {
+ var r = (codewords + cols - 1) / cols;
+ if (r < 3 || r > 90) {
+ continue;
+ }
+ var w = 17 * (cols + 4) + 1 + 2 * QUIET;
+ var h = r * ROW_HEIGHT + 2 * QUIET;
+ if (Codes.fits(w, h, px, _screenW, _screenH, _round)) {
+ columns = cols;
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ // Reed-Solomon mod 929 (pdf417gen's form), EC_STEP_WORDS words per step.
+ private function ecStep() as Void {
+ var n = _ec.size();
+ var f = _factors;
+ var end = _at + EC_STEP_WORDS < _words.size() ? _at + EC_STEP_WORDS : _words.size();
+ for (var i = _at; i < end; i++) {
+ var temp = (_words[i] + _ec[n - 1]) % 929;
+ for (var x = n - 1; x >= 0; x--) {
+ var word = x > 0 ? _ec[x - 1] : 0;
+ _ec[x] = (word + 929 - (temp * f[x]) % 929) % 929;
+ }
+ }
+ _at = end;
+ if (_at < _words.size()) {
+ return;
+ }
+ for (var x = n - 1; x >= 0; x--) {
+ var v = _ec[x];
+ _words.add(v > 0 ? 929 - v : 0);
+ }
+ _ec = [] as Array<Number>;
+ var width = 17 * (columns + 4) + 1;
+ _modules = new [width * rows]b;
+ _at = 0;
+ _phase = 2;
+ }
+
+ // A few rows per step: start, indicators, data, stop.
+ private function rowsStep() as Void {
+ var width = 17 * (columns + 4) + 1;
+ for (var k = 0; k < 4 && _at < rows; k++) {
+ var row = _at;
+ var cluster = _codes[row % 3];
+ var x = put(row, 0, START, 17, width);
+ x = put(row, x, cluster[leftIndicator(row, rows, columns, level)] as Number, 17, width);
+ for (var c = 0; c < columns; c++) {
+ x = put(row, x, cluster[_words[row * columns + c]] as Number, 17, width);
+ }
+ x = put(row, x, cluster[rightIndicator(row, rows, columns, level)] as Number, 17, width);
+ put(row, x, STOP, 18, width);
+ _at++;
+ }
+ if (_at < rows) {
+ return;
+ }
+ _codes = [] as Array<Array<Number>>;
+ _words = [] as Array<Number>;
+ var result = new Codes.Matrix(width, rows, QUIET, _modules);
+ result.rowHeight = ROW_HEIGHT;
+ _result = result;
+ _phase = 3;
+ }
+
+ // Draw count bits of pattern (most significant first) at column x of
+ // row `row`; returns the next column.
+ private function put(row as Number, x as Number, pattern as Number, count as Number, width as Number) as Number {
+ var at = row * width + x;
+ for (var i = 0; i < count; i++) {
+ _modules[at + i] = (pattern >> (count - 1 - i)) & 1;
+ }
+ return x + count;
+ }
+ }
+}
diff --git a/source/codes/Qr.mc b/source/codes/Qr.mc
index 8af0f5a..1fa69da 100644
--- a/source/codes/Qr.mc
+++ b/source/codes/Qr.mc
@@ -207,9 +207,8 @@ module Qr {
return err != null ? new Codes.CodeError(err) : job.result as MatrixCode;
}
- class QrJob extends Jobs.Job {
+ class QrJob extends Codes.MatrixJob {
- var error as String? = null;
var result as MatrixCode? = null;
private var _data as ByteArray;
@@ -237,7 +236,7 @@ module Qr {
// ec: 0 L, 1 M, 2 Q, 3 H. forceVersion 0 = smallest that fits;
// forceMask -1 = best by penalty.
function initialize(text as String, ec as Number, forceVersion as Number, forceMask as Number) {
- Job.initialize();
+ MatrixJob.initialize();
_data = Bytes.fromUtf8(text);
_ec = ec;
_forceMask = forceMask;
@@ -249,6 +248,12 @@ module Qr {
return _phase * 100 / 6;
}
+ // The code as a generic matrix with QR's 4-module quiet zone.
+ function matrix() as Codes.Matrix? {
+ var r = result;
+ return r != null ? new Codes.Matrix(r.size, r.size, 4, r.modules) : null;
+ }
+
function step(budget as Number) as Boolean {
if (_phase == 0) {
buildCodewords();