From 87ba93773ef363b137736cd8b680d3f8f8e1afbe Mon Sep 17 00:00:00 2001 From: Taka Date: Mon, 18 Jul 2022 05:35:19 +1000 Subject: [PATCH] Updated for v2.01 of the MMP eASLRB. --- asl_rulebook2/bin/fixup_mmp_pdf.py | 6 + asl_rulebook2/extract/content.py | 144 +++++--- .../extract/data/chapter-fixups.json | 6 +- .../extract/data/footnote-fixups.json | 85 +---- asl_rulebook2/extract/data/index-fixups.json | 7 + asl_rulebook2/extract/data/target-fixups.json | 103 +++++- .../extract/data/vo-note-fixups.json | 330 +++++------------- asl_rulebook2/extract/index.py | 2 +- asl_rulebook2/pdf.py | 4 +- asl_rulebook2/webapp/static/prepare.js | 2 +- 10 files changed, 325 insertions(+), 364 deletions(-) diff --git a/asl_rulebook2/bin/fixup_mmp_pdf.py b/asl_rulebook2/bin/fixup_mmp_pdf.py index 24f5250..bdc4f7a 100755 --- a/asl_rulebook2/bin/fixup_mmp_pdf.py +++ b/asl_rulebook2/bin/fixup_mmp_pdf.py @@ -91,6 +91,12 @@ def fixup_mmp_pdf( fname, output_fname, fix_zoom, optimize_web, rotate, log=None annot.Dest = make_page_destination( pdf, page_no, "XYZ", top=page_height ) log_msg( None, "" ) + # FUDGE! v2.01 of the MMP eASLRB PDF had a bodgy p104 (A62) that is a little wider than + # the rest of the pages (dumping the page dimensions using pikepdf didn't show anything unusual, + # but it was rendering differently in Firefox :-/), which was causing the h-scrollbar to appear. + # We hack the page width here to bring it in line with the others. + pdf.pages[ 104-1 ].MediaBox = [ 0, 0, 565, 792 ] + # save the updated PDF log_msg( "progress", "Saving the fixed-up PDF..." ) # NOTE: Setting a blank password will encrypt the file, but doesn't require the user diff --git a/asl_rulebook2/extract/content.py b/asl_rulebook2/extract/content.py index 6deb71c..60bb2f7 100755 --- a/asl_rulebook2/extract/content.py +++ b/asl_rulebook2/extract/content.py @@ -22,25 +22,36 @@ from asl_rulebook2.utils import parse_page_numbers, fixup_text, append_text, rem _DISABLE_SORT_ITEMS = [ "B40", # nb: to detect B31.1 NARROW STREET "A16", + "A54", "A55", "A56", "A58","A59","A60", # Chapter A footnotes (nb: page A61 is a mess wrt element order :-/) "B1", "B45", "B46", # Chapter B footnotes "C25", "C26", # Chapter C footnotes "D27", # Chapter D footnotes "E28", "E29", "E30", # Chapter E footnotes - "F20", "F21", # Chapter F footnotes + "F2", "F6", "F7", "F8", "F9", "F10", "F11", "F16", "F17", + "F18", "F19", # Chapter F footnotes "G48", "G49", "G50", # Chapter G footnotes "H9", # Chapter H footnotes - 429,431,432,433,434,435, # Italian vehicle notes - 436,437,438,439, # Italian ordnance notes + # Chapter H vehicles/ordnace: + 359, 360, + 363, 374, + 376, 383, 387, 388, 390, 393, + 408, 417, 426, 429, + 434, 436, 438, + 449, 452, 453, + 467, 468, 469, + 486, 492, + 529, 530, 531, 532, + 547, 549, 589, ] _DEFAULT_ARGS = { - "chapter-a": "42-102", "chapter-b": "109-154", "chapter-c": "158-183", "chapter-d": "187-213", - "chapter-e": "216-245", "chapter-f": "247-267", "chapter-g": "270-319", "chapter-h": "322-324,326-330", - "chapter-j": "593", - "chapter-w": "647-664", - "content_vp_left": 0, "content_vp_right": 565, "content_vp_top": 715, "content_vp_bottom": 28, # viewport + "chapter-a": "43-104", "chapter-b": "111-156", "chapter-c": "161-186", "chapter-d": "191-217", + "chapter-e": "221-250", "chapter-f": "253-271", "chapter-g": "275-324", "chapter-h": "327-329,331-335", + "chapter-j": "599", + "chapter-w": "653-670", + "content_vp_left": 0, "content_vp_right": 600, "content_vp_top": 715, "content_vp_bottom": 28, # viewport "disable-sort-items": ",".join( str(si) for si in _DISABLE_SORT_ITEMS ) } @@ -48,35 +59,35 @@ _DEFAULT_ARGS = { # - the order of the nationality + V/O types # - the page numbers themselves (so that they get parsed) _VO_NOTE_SECTIONS = [ - [ "german", "vehicles", "330,332,334-343", True ], - [ "german", "ordnance", "344-348", True ], - [ "russian", "vehicles", "348,350-355", True ], - [ "russian", "ordnance", "356-358", True ], - [ "russian", "vehicles", "362,364-368", False ], - [ "russian", "ordnance", "369", False ], - [ "american", "vehicles", "371,373-383", True ], - [ "american", "ordnance", "385-389", True ], - [ "british", "vehicles", "395,398-417", True ], - [ "british", "ordnance", "419-423", True ], - [ "italian", "vehicles", "429,431-435", True ], - [ "italian", "ordnance", "436-439", True ], - [ "japanese", "vehicles", "443-448", True ], - [ "japanese", "ordnance", "448-452", True ], - [ "chinese", "vehicles", "456-459", True ], - [ "chinese", "ordnance", "459-463", True ], - [ "landing-craft", "vehicles", "467-468", True ], - [ "french", "vehicles", "470,472-480", True ], - [ "french", "ordnance", "482-487", True ], - [ "allied-minor", "vehicles", "492-493,495-500", True ], - [ "allied-minor", "ordnance", "501-504", True ], - [ "axis-minor", "vehicles", "506,508-515", True ], - [ "axis-minor", "ordnance", "516,518-527", True ], - [ "finnish", "vehicles", "536,538-541", True ], - [ "finnish", "ordnance", "543,545-549", True ], - [ "un-forces", "vehicles", "554,556-565", True ], - [ "un-forces", "ordnance", "567-570", True ], - [ "communist-forces", "vehicles", "580", True ], - [ "communist-forces", "ordnance", "581-585", True ], + [ "german", "vehicles", "335,337,339-348", True ], + [ "german", "ordnance", "349-353", True ], + [ "russian", "vehicles", "353,355-360", True ], + [ "russian", "ordnance", "361-363", True ], + [ "russian", "vehicles", "367,369-373", False ], + [ "russian", "ordnance", "374", False ], + [ "american", "vehicles", "376,378-388", True ], + [ "american", "ordnance", "390-394", True ], + [ "british", "vehicles", "400,403-422", True ], + [ "british", "ordnance", "424-428", True ], + [ "italian", "vehicles", "434,436-440", True ], + [ "italian", "ordnance", "441-444", True ], + [ "japanese", "vehicles", "449-454", True ], + [ "japanese", "ordnance", "454-458", True ], + [ "chinese", "vehicles", "462-465", True ], + [ "chinese", "ordnance", "465-469", True ], + [ "landing-craft", "vehicles", "473-474", True ], + [ "french", "vehicles", "476,478-486", True ], + [ "french", "ordnance", "488-493", True ], + [ "allied-minor", "vehicles", "498-499,501-506", True ], + [ "allied-minor", "ordnance", "507-510", True ], + [ "axis-minor", "vehicles", "512,514-521", True ], + [ "axis-minor", "ordnance", "522,524-533", True ], + [ "finnish", "vehicles", "542,544-547", True ], + [ "finnish", "ordnance", "549,551-555", True ], + [ "un-forces", "vehicles", "560,562-571", True ], + [ "un-forces", "ordnance", "573-576", True ], + [ "communist-forces", "vehicles", "586", True ], + [ "communist-forces", "ordnance", "587-591", True ], ] # --------------------------------------------------------------------- @@ -103,7 +114,7 @@ class ExtractContent( ExtractBase ): self._footnote_fixups = load_fixup( "footnote-fixups.json" ) self._vo_note_fixups = load_fixup( "vo-note-fixups.json" ) - def extract_content( self, pdf ): + def extract_content( self, pdf ): #pylint: disable=too-many-branches """Extract content from the MMP eASLRB.""" # figure out which pages to process @@ -169,7 +180,10 @@ class ExtractContent( ExtractBase ): def elem_filter( elem ): return isinstance( elem, LTChar ) sort_elems = self._curr_pageid not in disable_sort_items and str(page_no) not in disable_sort_items - for _, elem in PageElemIterator( lt_page, elem_filter=elem_filter, sort_elems=sort_elems ): + centre_adjust = 35 if self._curr_footnote is not None and self._curr_chapter != "W" else 0 + for _, elem in PageElemIterator( lt_page, centre_adjust=centre_adjust, + elem_filter=elem_filter, sort_elems=sort_elems + ): # skip problematic elements if elem.fontname == "OYULKV+MyriadPro-Regular": @@ -238,6 +252,17 @@ class ExtractContent( ExtractBase ): # loop back to process the next element self._prev_elem = elem + # check for extra targets that need to be added in + extra_targets = self._target_fixups.get( self._curr_pageid, {} ).pop( "extras", [] ) + if extra_targets: + if not self._target_fixups[ self._curr_pageid ]: + del self._target_fixups[ self._curr_pageid ] + for extra_target in extra_targets: + self.targets[ extra_target["ruleid"] ] = { + "caption": extra_target.get("caption",""), + "page_no": page_no, "pos": extra_target["pos"] + } + # add the last caption/footnote (if they haven't already been done) self._save_footnote() if curr_caption: @@ -277,7 +302,7 @@ class ExtractContent( ExtractBase ): fixup = self._target_fixups.get( self._curr_pageid, {} ).get( caption_text ) if fixup: # yup - make it so - fixup[ "instances" ] = fixup.get("instances",1) - 1 + fixup[ "instances" ] = fixup.get( "instances", 1 ) - 1 if fixup["instances"] <= 0: self._target_fixups[ self._curr_pageid ].pop( caption_text ) if not self._target_fixups[ self._curr_pageid ]: @@ -325,7 +350,14 @@ class ExtractContent( ExtractBase ): """Process an element while we're parsing footnotes.""" # check if we've found the start of a new footnote if self._is_bold( elem ): - if elem.get_text().isdigit() and self._is_start_of_line( elem, lt_page ): + # FUDGE! The new Chapter F has things like "13. 7.1 SAND", which fooled the code into thinking + # that the "7" was the start of a new footnote (because it's a digit, and near the start + # of the line :-/), so we check for that case here. + def check(): + if self._curr_chapter != "F" or not self._curr_footnote: + return False + return any( self._curr_footnote[0] == "{}. ".format(i) for i in range(10,15+1) ) + if elem.get_text().isdigit() and self._is_start_of_line( elem, lt_page ) and not check(): # yup - save the current footnote, start collecting the new one self._save_footnote() elem_pos = ( elem.x0, elem.y1 ) @@ -460,7 +492,7 @@ class ExtractContent( ExtractBase ): # check for the credits at the end of the Chapter F footnotes if self._curr_chapter == "F": - pos = content.find( "WEST OF ALAMEIN CREDITS" ) + pos = content.find( "HOLLOW LEGIONS CREDITS" ) if pos > 0: content = content[:pos] # check for the start of the vehicle notes at the end of the Chapter H footnotes @@ -477,6 +509,21 @@ class ExtractContent( ExtractBase ): "raw_content": orig_content } ) self._curr_footnote = None + split = self._footnote_fixups.get( self._curr_chapter, {} ).pop( "split:"+footnote_id, None ) + if split: + content = self._footnotes[self._curr_chapter][-1]["content"] + pos = content.find( split["split_text"] ) + if pos < 0: + self.log_msg( "warning", "Can't find footnote split: {}", split["split_text"] ) + else: + new_content = content[ pos + len( split["split_text"] ) : ] + self._footnotes[self._curr_chapter][-1]["content"] = content[:pos].strip() + self._footnotes[ self._curr_chapter ].append( { + "footnote_id": split["new_footnote_id"], + "captions": split["new_captions"], + "content": new_content.strip(), + "raw_content": None + } ) def _save_vo_note( self, caption, page_no, page_pos ): """Save an extracted vehicle/ordnance note.""" @@ -494,7 +541,9 @@ class ExtractContent( ExtractBase ): if not self._vo_note_fixups["skips"]: del self._vo_note_fixups["skips"] return - if caption.isdigit() and page_no not in (354, 417): + if caption.isdigit(): + return + if not any( ch.isalpha() for ch in caption ): return def apply_fixups( vo_note_id, caption ): @@ -510,6 +559,9 @@ class ExtractContent( ExtractBase ): vo_note_id = fixup["new_vo_note_id"] if "new_caption" in fixup: caption = fixup["new_caption"] + if "new_page_pos" in fixup: + nonlocal page_pos + page_pos = fixup["new_page_pos"] return vo_note_id, caption def cleanup_fixups( nat, vo_type ): @@ -567,7 +619,11 @@ class ExtractContent( ExtractBase ): elif base_note_id == prev_base_note_id and "." in vo_note_id: pass # nb: this is to allow things like "9.1" following "9" else: - return # nb: we got some junk that can be ignored + # NOTE: We used to return here, which ignored a lot of junk, but it has the unfortunate + # side-effect of, if we missed an entry, then all entries after that would also be missed. + # With v2.01 of the eASLRB, there were changes to the layout that weren't really fixable + # using the fixup data files, so we bit the bullet and manually ignore the junk :-/ + pass # save the V/O note self._vo_notes[ nat ][ vo_type ].append( { diff --git a/asl_rulebook2/extract/data/chapter-fixups.json b/asl_rulebook2/extract/data/chapter-fixups.json index 23e465e..b21d563 100644 --- a/asl_rulebook2/extract/data/chapter-fixups.json +++ b/asl_rulebook2/extract/data/chapter-fixups.json @@ -25,7 +25,11 @@ "replace": { "B32": [ "RAILROADS10", "Railroads" ], "B34": [ "TOWERS18", "Towers" ], - "B36": [ "PREPARED FIRE ZONE2", "Prepared Fire Zone" ] + "B36": [ "PREPARED FIRE ZONE2", "Prepared Fire Zone" ], + "F1": [ "OPEN GROUND", "Open Ground" ], + "F2": [ "SCRUB", "Scrub" ], + "F4": [ "DEIRS", "Deirs" ], + "F5": [ "WADIS", "Wadis" ] } } diff --git a/asl_rulebook2/extract/data/footnote-fixups.json b/asl_rulebook2/extract/data/footnote-fixups.json index fff1ca8..765674e 100644 --- a/asl_rulebook2/extract/data/footnote-fixups.json +++ b/asl_rulebook2/extract/data/footnote-fixups.json @@ -2,20 +2,14 @@ "A": { - "10A": [ - [ "OneHalfFP", "One-Half FP" ], - [ "firstappearedintheASLAnnual'89.(In1998,bothwerereprintedin Classic ASL.)", "first appeared in the ASL Annual '89. (In 1998, both were reprinted in Classic ASL.)" ], - [ "One of the several criticisms", "

One of the several criticisms" ] - ], - "12": [ [ "TEMto", "TEM to" ] ], - "14": [ - [ "bipodmounted", "bipod-mounted" ], - [ "volume o f fire", "volume of fire" ] - ], - "17": [ [ "adistinct", "a distinct" ] ], - "19" : [ [ "wellsited", "well-sited" ] ], - "32": [ [ "HWunits", "HW units" ] ], - "33": [ [ "multiLocation", "multi-Location" ] ], + "10A": [ [ "One of the several criticisms", "

One of the several criticisms" ] ], + "14": [ [ "bipodmounted", "bipod-mounted" ] ], + "19": [ [ "wellsited", "well-sited" ] ], + "split:31A": { + "split_text": "31B. 25.212 RUSSIAN EARLY WAR DOCTRINE:", + "new_footnote_id": "31B", + "new_captions": [ [ "A25.212", "RUSSIAN EARLY WAR DOCTRINE" ] ] + }, "35": [ [ "The original printing", "

The original printing" ] ], "37": [ [ "- Winter War (vs Soviet Union) 30 November 1939 - 13 March 1940- Continuation War (vs Soviet Union) 25 June 1941 - 4 September 1944- Lapland War (vs Germany) 15 September 1944 - 27 April 1945", "

" ] @@ -26,16 +20,12 @@ [ "Slovakia: Urged on", "

Slovakia: Urged on" ], [ "German-Croatian units in Russia:", "

German-Croatian units in Russia:" ], [ "Italian-Croatian units in Russia:", "

Italian-Croatian units in Russia:" ], + [ "existing GermanCroatian units", "existing German-Croatian units" ], [ "Croatian units in Yugoslavia:", "

Croatian units in Yugoslavia:" ], - [ "CroatianArmyunitswereengagedprimarilyinanti-partisanactivities,fightingmostly", "Croatian Army units were engaged primarily in anti-partisan activities, fighting mostly" ], - [ "Bulgaria: Bulgaria", "

Bulgaria: Bulgaria" ], - [ "WhiletheriflecompanydidnothaveaninherentHeavyWeapons(HW)platoon,it", "While the rifle company did not have an inherent Heavy Weapons (HW) platoon, it"] + [ "Bulgaria: Bulgaria", "

Bulgaria: Bulgaria" ] ], - "39": [ [ "generallyapply", "generally apply" ] ], - "41": [ [ "ViceAdmiral", "Vice-Admiral" ] ], - "43": [ - [ "ALLIEDMINORS", "ALLIED MINORS" ], - [ "BARrather", "BAR rather" ] + "42": [ + [ "twoman", "two-man" ] ] }, @@ -107,51 +97,14 @@ "F": { + "split:7": { + "split_text": "8. 5.1 WADIS:", + "new_footnote_id": "8", + "new_captions": [ [ "F5.1", "WADIS" ] ] + }, "12": [ [ "non- entrenched", "non-entrenched" ] ], - "19": [ - [ "Inthewinternight,thenear-freezingtemperaturecauseddewtoform.", "In the winter night, the near-freezing temperature caused dew to form. " ], - [ "Thenextmorningathickmistoftenformedasthesun evaporateditagain.", "The next morning a thick mist often formed as the sun evaporated it again. " ], - [ "Thiscouldhappeneveninthesummertimeundertheproperenvironmentalconditions,", "This could happen even in the summertime under the proper environmental conditions, " ], - [ "butsincethiswasamuchlessfrequentoccurrenceithasbeen ignored.", "but since this was a much less frequent occurrence it has been ignored." ] - ], - "21": [ - [ "Playerswillprobablyfinditmoreconvenienttoinstead", "Players will probably find it more convenient to instead" ], - [ "addathird,different-coloreddietothisTH/IFTDR,", "add a third, different-colored die to this TH/IFT DR, " ], - [ "usingittodeterminetheDust DRM.", "using it to determine the Dust DRM." ], - [ "Thefamiliarterm\"subsequentdr\"wasusedintherulebecauseitobviates theneed", "The familiar term \"subsequent dr\" was used in the rule because it obviates the need" ], - [ "a\"new\"concept", "a \"new\" concept" ], - [ "thatof rolling athird diesimultaneously", "that of rolling a third die simultaneously" ] - ], - "22": [ - [ "theDustcounter\"follows\"thevehicleasit movesfromhex to hex", "the Dust counter \"follows\" the vehicle as it moves from hex to hex" ], - [ "itexpends", "it expends " ], - [ "two MPeach timeitdoesso", " two MP each time it does so" ] - ], - "23": [ - [ "Anotherwind-relatedaspectoftheNorthAfricanenvironmentisthedesertsandstorm,", "Another wind-related aspect of the North African environment is the desert sandstorm, " ], - [ "orkhamsininArabic.", "or khamsin in Arabic. " ], - [ "ChapterFincludesnospecial rulesforitbecause,", "Chapter F includes no special rules for it because, " ], - [ "withvisibilitycutbythestormtoaslittleasthreeyards,", "with visibility cut by the storm to as little as three yards, " ], - [ "allactivitiesgenerallywerereducedtoseekingcoverfromthesandblastingwindandchoking dust.", "all activities generally were reduced to seeking cover from the sandblasting wind and choking dust. " ], - [ "However,thegamedoesnotignorethepossibilityofakhamsin'soccurrence.", "However, the game does not ignore the possibility of a khamsin's occurrence. " ], - [ "The propercombinationofWeather,EC,WindandGustsinaDYOscenariocancreateits effects,", "The proper combination of Weather, EC, Wind and Gusts in a DYO scenario can create its effects, " ], - [ "andtheprobabilityofitsoccurrenceisgreatestinascenariosetinspringor summer", "and the probability of its occurrence is greatest in a scenario set in spring or summer" ], - [ "thetimewhen khamsinsoccurred mostfrequently.", "the time when khamsins occurred most frequently." ] - ], - "24": [ - [ "Thisoverlay isused in aHOLLOW LEGIONS scenario.", "This overlay is used in a HOLLOW LEGIONS scenario." ] - ], - "25": [ - [ "ThefamousNorthAfricanescarpmentsaresimilarto cliffs,", "The famous North African escarpments are similar to cliffs, " ], - [ "butwithlesssteep(andveryeroded)slopes.", "but with less steep (and very eroded) slopes. " ], - [ "Somearesixhundredfeethigh", "Some are six hundred feet high" ], - [ "thoughgenerallytheirheightsrangefromonehundredtotwohundredfeet.", "though generally their heights range from one hundred to two hundred feet. " ], - [ "Theirsignificanceinthedesertwarlaymainlyinthattheywerecommandingheights,", "Their significance in the desert war lay mainly in that they were commanding heights, " ] , - [ "defensivepositionsforinfantry,", "defensive positions for infantry, " ], - [ "andgreatlyrestrictedvehicularmovementacrossthem", "and greatly restricted vehicular movement across them" ], - [ "Hencetheywereoftenthesceneofheavyfighting,", "Hence they were often the scene of heavy fighting, " ], - [ "especiallywherecrossedbya road", "especially where crossed by a road" ] - ] + "19": [ [ "nearfreezing", "near-freezing" ] ] + }, "G": { diff --git a/asl_rulebook2/extract/data/index-fixups.json b/asl_rulebook2/extract/data/index-fixups.json index 2f27d93..23a2e98 100644 --- a/asl_rulebook2/extract/data/index-fixups.json +++ b/asl_rulebook2/extract/data/index-fixups.json @@ -66,6 +66,13 @@ "new_content": "(Any fire attack requiring a LOS from the firer which does not use Indirect Fire): C.1, C9.1 [Intervening Units: A6.6] [LC: G12.61-.62, G12.671]" }, +"Dispersed Smoke": { + "replace": [ + [ "SmokeGrenadesNA", "Smoke Grenades NA" ], + [ "VehicularSmoke", "Vehicular Smoke" ] + ] +}, + "Dogfight": { "old_content": "(AerialCombat):E7.22", "new_content": "(Aerial Combat): E7.22" diff --git a/asl_rulebook2/extract/data/target-fixups.json b/asl_rulebook2/extract/data/target-fixups.json index 70d2bce..04b2f36 100644 --- a/asl_rulebook2/extract/data/target-fixups.json +++ b/asl_rulebook2/extract/data/target-fixups.json @@ -108,18 +108,10 @@ } }, -"A54": { - "25.53 FREEFRENCH:": { - "new_ruleid": "A25.53", - "new_caption": "FREE FRENCH" - } -}, - "A55": { - "26.VICTORYCONDITIONS": { - "new_ruleid": "A26", - "new_caption": "VICTORY CONDITIONS" - } + "extras": [ + { "ruleid": "A25.71", "caption": "LEADERS", "pos": [172,714] } + ] }, "B1": { @@ -132,7 +124,10 @@ "T10": { "new_ruleid": null }, "W5": { "new_ruleid": null }, "V5": { "new_ruleid": null }, - "X6": { "new_ruleid": null } + "X6": { "new_ruleid": null }, + "Y6": { "new_ruleid": null }, + "Y7": { "new_ruleid": null }, + "Y10": { "new_ruleid": null } }, "B4": { @@ -348,10 +343,86 @@ "3)": { "new_ruleid": null } }, -"F18": { - "D3": { "new_ruleid": null }, - "W1": { "new_ruleid": null }, - "H4": { "new_ruleid": null } +"F1": { + "8½ 1 3 3 5 1 7": { "new_ruleid": null }, + "1 1": { "new_ruleid": null }, + "17": { "new_ruleid": null } +}, + +"F2": { + "1. OPEN GROUND 1.1": { + "new_ruleid": "F1", + "new_caption": "OPEN GROUND" + }, + "2. SCRUB 2.1": { + "new_ruleid": "F2", + "new_caption": "SCRUB" + }, + "extras": [ + { "ruleid": "F1.1", "pos": [18,330] }, + { "ruleid": "F2.1", "pos": [18,173] }, + { "ruleid": "F3", "caption": "HAMMADA", "pos": [294,592] }, + { "ruleid": "F3.1", "pos": [294,582] } + ] +}, + +"F3": { + "4. DEIRS 7 4.1": { + "new_ruleid": "F4", + "new_caption": "DEIRS" + }, + "5. WADIS 8 5.1": { + "new_ruleid": "F5", + "new_caption": "WADIS" + }, + "extras": [ + { "ruleid": "F4.1", "pos": [66,109] }, + { "ruleid": "F5.1", "pos": [342,166] } + ] +}, + +"F6": { + "3 TEM:": { + "new_ruleid": "F5.423", + "new_caption": "TEM" + } +}, + +"F7": { + "extras": [ + { "ruleid": "F6", "caption": "HILLOCKS", "pos": [66,276] } + ] +}, + +"F8": { + "extras": [ + { "ruleid": "F7", "caption": "SAND", "pos": [294,531] }, + { "ruleid": "F7.1", "pos": [294,520] } + ] +}, + +"F12": { + "11.4": { + "new_ruleid": "F11.4", + "new_caption": "ENVIRONMENTAL CONDITIONS" + }, + "18": { "new_ruleid": null }, + "extras": [ + { "ruleid": "F11.3", "caption": "TIME OF DAY", "pos": [294,529] } + ] +}, + +"F14": { + "20 11.7 DUST:": { + "new_ruleid": "F11.7", + "new_caption": "DUST" + }, + "21": { "new_ruleid": null }, + "22": { "new_ruleid": null } +}, + +"F15": { + "23": { "new_ruleid": null } }, "G30": { diff --git a/asl_rulebook2/extract/data/vo-note-fixups.json b/asl_rulebook2/extract/data/vo-note-fixups.json index 4339da1..1a85fa8 100644 --- a/asl_rulebook2/extract/data/vo-note-fixups.json +++ b/asl_rulebook2/extract/data/vo-note-fixups.json @@ -1,23 +1,27 @@ { "skips": { - "382": [ "1, 3" ], - "429": [ "^1,660,", "^1and Fiat 3000", "^9/43 armistice", "^4/41 (.9)" ], - "431": [ "^1, for East Africa", "^9/42 (1.4)," ], - "432": [ "^1 (l.2),", "^1. Sources vary" ], - "434": [ "1-", "^1.5 for 11/41-6/42," ], - "438": [ "^1/41-5/43" ], - "439": [ "1 (1", "^1/43 ( 1.2),", "^1/43 (1.3),", "^1/42-5/43." ], - "492": [ "1B11CE/FPNA", "1B11CE/FPNA" ], - "493": [ "1T", "1B" ], - "496": [ "1B" ], - "501": [ "1h-d" ], - "502": [ "1s5", "1s5" ], - "503": [ "1AP5", "1s6" ], - "504": [ "^1.3)" ], - "514": [ "1.4 for 45" ], - "556": [ "1#" ], - "560": [ "1, 3" ] + "342": [ "5.1 GSW 39H(f) PaK:" ], + "345": [ "4 FP", "4 FP", "6 FP", "4 FP", "4 FP", "4 FP", "6 FP", "4 FP" ], + "355": [ "26S M37/39:" ], + "422": [ "30-cwt Lorry:", "3-Ton Lorry:" ], + "463": [ "243M2" ], + "466": [ "^50mm RM obr. 38," ], + "468": [ "20/65, & 2cm FlaK 30:" ], + "485": [ "4, M4A1, & M4A2 Medium Tanks:" ], + "498": [ "11:Stall", "11:Stall", "1B11CE/FPNA", "1B11CE/FPNA", "2x" ], + "499": [ "1T", "1B", "2B11B11" ], + "501": [ "4PP", "5PPcs 5", "5PP" ], + "502": [ "1B" ], + "504": [ "2/2CS 6" ], + "505": [ "9PPcs 2", "9PP*" ], + "506": [ "29PP", "21PP", "9PP", "9PP" ], + "507": [ "4M", "1h-d", "11M" ], + "508": [ "8M", "12M", "8M", "1s5", "11M", "12M" ], + "509": [ "3/42 RR1", "10M", "8M", "9M(9/39-s6", "5/40 RF 1.5", "4/41 RF 1.4", "8M", "6M", "^10/39 RF" ], + "510": [ "^1.3)", "^5/40 RF", "8M" ], + "519": [ "8/43; a", "^38(t)E is 1.3" ], + "520": [ "1.4 for 45", "44, and 1.4 for 45" ] }, "german": { @@ -63,16 +67,22 @@ "new_caption": "2cm & 3.7cm FlaK LKW" }, "96": { - "old_caption": "Opel 6700 &Buessing-NAG", + "old_caption": "Opel 6700 &", "new_caption": "Opel 6700 & Buessing-NAG 4500" }, "add": [ { "_comment_": "This gets parsed as '4' and '5.1 GSW 39H(f) PaK' :-/", - "vo_note_id": "45.1", "caption": "GSW 39H(f) PaK", "page_no": 337, "page_pos": [380,561] + "vo_note_id": "45.1", "caption": "GSW 39H(f) PaK", "page_no": 342, "page_pos": [380,561] }, - { "vo_note_id": "37.1", "caption": "Sturmtiger", "page_no": 532, "page_pos": [118,640] }, - { "vo_note_id": "88.1", "caption": "SdKfz 10/5", "page_no": 532, "page_pos": [399,713] } + { "vo_note_id": "37.1", "caption": "Sturmtiger", "page_no": 538, "page_pos": [118,640] }, + { "vo_note_id": "88.1", "caption": "SdKfz 10/5", "page_no": 538, "page_pos": [399,713] } ] +}, +"ordnance": { + "29": { + "old_caption": "3.7cm FlaK 43: O", + "new_caption": "3.7cm FlaK 43" + } } }, @@ -99,7 +109,9 @@ "new_caption": "ISU-122 & ISU-152" }, "add": [ - { "vo_note_id": "12.1", "caption": "T-28E M40(L)", "page_no": 364, "page_pos": [394,289] } + { "vo_note_id": "47", "caption": "", "page_no": 359, "page_pos": [461,580] }, + { "vo_note_id": "12.1", "caption": "T-28E M40(L)", "page_no": 369, "page_pos": [394,289] }, + { "vo_note_id": "48", "caption": "Stuart III(a)", "page_no": 371, "page_pos": [394,515] } ] } }, @@ -111,7 +123,7 @@ "new_caption": "M4A3E2 & M4A3E2 (L) Medium Tanks" }, "17": { - "old_caption": "M4(105) & M4A3(105) MediumTanks", + "old_caption": "M4(105) & M4A3(105) Medium", "new_caption": "M4(105) & M4A3(105) Medium Tanks" } } @@ -120,7 +132,7 @@ "british": { "vehicles": { "2": { - "old_caption": "(A17) Tetrarch & Tetrarch CS[Light Tanks Mk VII & Mk VII CS]", + "old_caption": "(A17) Tetrarch & Tetrarch CS", "new_caption": "(A17) Tetrarch & Tetrarch CS [Light Tanks Mk VII & Mk VII CS]" }, "6": { @@ -132,211 +144,34 @@ "new_caption": "(A12) Matilda II & II CS [Infantry Tank Mk II]" }, "36": { - "old_caption": "Valentine & Churchill Bridgelay-ers", + "old_caption": "Valentine & Churchill Bridgelay-", "new_caption": "Valentine & Churchill Bridgelayers" }, "45": { "old_caption": "Humber III & Otter Light Re-connaissance Cars", "new_caption": "Humber III & Otter Light Reconnaissance Cars" }, - "82": { - "old_caption": "", - "new_caption": "30-cwt Lorry" + "54": { + "old_caption": "Staghound I(a) & II(a) Armoured", + "new_caption": "Staghound I(a) & II(a) Armoured Cars" }, - "83": { - "old_caption": "", - "new_caption": "3-Ton Lorry" + "add": [ + { "vo_note_id": "82", "caption": "30-cwt Lorry", "page_no": 422, "page_pos": [116,475] }, + { "vo_note_id": "83", "caption": "3-Ton Lorry", "page_no": 422, "page_pos": [116,287] } + ] +}, +"ordnance": { + "16": { + "old_caption": "OBL 4.5-in. Gun & 5.5-in. Gun-", + "new_caption": "OBL 4.5-in. Gun & 5.5-in. Gun-Howitzer" } } }, "italian": { "vehicles": { - "1": { - "old_caption": "LS/21 & LS/3", - "new_caption": "L5/21 & L5/30" - }, - "2": { - "old_caption": "^L3/35: Derived from", - "new_caption": "L3/35" - }, - "3": { - "old_caption": "^L3 aa: Some L3", - "new_caption": "L3 aa" - }, - "4": { - "old_caption": "^L3 cc: During the early months", - "new_caption": "L3 cc" - }, - "5": { - "old_caption": "^L3 Lf: Development of", - "new_caption": "L3 Lf" - }, - "6": { - "old_caption": "^L6/40: Designed to replace", - "new_caption": "L6/40" - }, - "7": { - "old_caption": "^Mll/39: This tank carried", - "new_caption": "M11/39" - }, - "8": { - "old_caption": "^Ml3/40: Replacing the", - "new_caption": "M13/40" - }, - "9": { - "old_caption": "^M14/41: This tank,", - "new_caption": "M14/41" - }, - "10": { - "old_caption": "^M15/42: This, the last version", - "new_caption": "M15/42" - }, - "11": { - "old_caption": "^MR/35(f): The Germans provided", - "new_caption": "MR/35(f)" - }, - "12": { - "old_caption": "Semovente M40 & M41 da", - "new_caption": "Semovente M40 & M41 da 75/18" - }, - "13": { - "old_caption": "^Semovente M42 da 75/1&75/32: The last model", - "new_caption": "Semovente M42 da 75/18 & 75/32" - }, - "14": { - "old_caption": "^Semovente M43 da 105/25: Nicknathe", - "new_caption": "Semovente M43 da 105/25" - }, - "15": { - "old_caption": "Semovente L40 da 47/32: The SMV", - "new_caption": "Semovente L40 da 47/32" - }, - "16": { - "old_caption": "^Semovente M41M da 90/53: This AFV", - "new_caption": "Semovente M41M da 90/53" - }, - "18": { - "old_caption": "^Lince: The Lince (Lynx)", - "new_caption": "Lince" - }, - "19": { - "old_caption": "^Lancia lZM: In late 1912", - "new_caption": "Lancia 1ZM" - }, - "20": { - "old_caption": "^Fiat 611A & 611BThese armoredcars", - "new_caption": "Fiat 611A & 611B" - }, - "21": { - "old_caption": "^AB 40 & AB41These two auto", - "new_caption": "AB 40 & AB 41" - }, - "22": { - "old_caption": "^Autoprotetto S37: This APC", - "new_caption": "Autoprotetto S37" - }, - "23": { - "old_caption": "Autocannoni da", - "new_caption": "Autocannoni da 20/65(b) & 65/17(b)" - }, - "24": { - "old_caption": "Autocannoni da", - "new_caption": "Autocannoni da 75/27 CK & 90/53" - }, - "25": { - "old_caption": "^TL 37, TM 40 &TP 32", - "new_caption": "TL 37, TM 40 & TP 32" - }, - "26": { - "old_caption": "^Autocarretta: As the portee", - "new_caption": "Autocarretta" - }, - "27": { - "old_caption": "^Fiat 508 MC: Derived from", - "new_caption": "Fiat 508 MC" - }, - "28": { - "old_caption": "^Autocarri L, M & P: The ItalianArmy", - "new_caption": "Autocarri L, M & P" - } -}, -"ordnance": { - "1": { - "old_caption": "^Mortaio da 45 \"Brixia\": This weapon,", - "new_caption": "Mortaio da 45 \"Brixia\"" - }, - "2": { - "old_caption": "^Mortaio da 81/14: First usedi", - "new_caption": "Mortaio da 81/14" - }, - "3": { - "old_caption": "^Fucile-cc S: Like several other", - "new_caption": "Fucile-cc S" - }, - "4": { - "old_caption": "^Cannone-cc da 37/45: This was", - "new_caption": "Cannone-cc da 37/45" - }, - "5": { - "old_caption": "^Cannone da 47/32: This was", - "new_caption": "Cannone da 47/32" - }, - "6": { - "old_caption": "^Cannone da 65/17: This was", - "new_caption": "Cannone da 65/17" - }, - "7": { - "old_caption": "^Cannone da 70/15: This", - "new_caption": "Cannone da 70/15" - }, - "8": { - "old_caption": "^Obice da 75/13: The Skoda", - "new_caption": "Obice da 75/13" - }, - "9": { - "old_caption": "^Cannone da 75/27: This was", - "new_caption": "Cannone da 75/27" - }, - "10": { - "old_caption": "^Obice da 75/18: This game piece", - "new_caption": "Obice da 75/18" - }, - "11": { - "old_caption": "^Cannone da 75/32: The 75/32", - "new_caption": "Cannone da 75/32" - }, - "12": { - "old_caption": "^Obice da 100/17: Another old", - "new_caption": "Obice da 100/17" - }, - "13": { - "old_caption": "^Cannone da 105/28: This was", - "new_caption": "Cannone da 105/28" - }, - "14": { - "old_caption": "^Obice da 149/13: This piece", - "new_caption": "Obice da 149/13" - }, - "15": { - "old_caption": "^Cannone da 149/35: Another", - "new_caption": "Cannone da 149/35" - }, - "16": { - "old_caption": "^Cannone da 149/40: To replace", - "new_caption": "Cannone da 149/40" - }, - "17": { - "old_caption": "^Cannone-mitragliera da 20/65: Thiswas", - "new_caption": "Cannone-mitragliera da 20/65" - }, - "18": { - "old_caption": "^Cannone-aa da 75/39: This was", - "new_caption": "Cannone-aa da 75/39" - }, "add": [ - { "vo_note_id": "19", "caption": "Cannone-aa da 75/46", "page_no": 439, "page_pos": [283,42] }, - { "vo_note_id": "20", "caption": "Cannone-aa da 90/53", "page_no": 439, "page_pos": [384,541] } + { "vo_note_id": "12", "caption": "Semovente M40 & M41 da 75/18", "page_no": 437, "page_pos": [442,715] } ] } }, @@ -383,17 +218,16 @@ "new_caption": "Year-3 Type 14cm Naval Seacoast Gun" }, "20": { - "old_caption": "Type 93 Twin-Mount High-Angle Ma-chine Gun", + "old_caption": "Type 93 Twin-Mount High-Angle Ma-", "new_caption": "Type 93 Twin-Mount High-Angle Machine Gun" }, - "22": { - "old_caption": "Type 96 Single-, Twin-, & Triple-Mount Naval High-Angle Machine Can-", - "new_caption": "Type 96 Single-, Twin-, & Triple-Mount Naval High-Angle Machine Cannons" - }, "24": { "old_caption": "Year-10 Type 12cm Naval High-AngleGun", "new_caption": "Year-10 Type 12cm Naval High-Angle Gun" - } + }, + "add": [ + { "vo_note_id": "22", "caption": "Type 96 Single-, Twin-, & Triple-Mount Naval High-Angle Machine Cannons", "page_no": 458, "page_pos": [395,713] } + ] } }, @@ -433,31 +267,51 @@ "french": { "vehicles": { "20": { - "old_caption": "Autocanon de 75 mle 97 & Autocanonde 75 Conus(b)", - "new_caption": "Autocanon de 75 mle 97 & Autocanon de 75 Conus(b)" + "old_caption": "CA, & Autocanon de 25 CA", + "new_caption": "Autocanon de 75 mle 97 & Autocanon de 75 Conus(b)", + "new_page_pos": [395,715] }, "21": { "old_caption": "Camion de Mitrailleuse Contre-Avions, Camion de 13.2 CAJ, Camion de", "new_caption": "Camion de Mitrailleuse Contre-Avions, Camion de 13.2 CAJ, Camion de 20 CA, & Autocanon de 25 CA" }, "36": { - "old_caption": "Peugeot 202, Citroën 23, & RenaultAGR2", + "old_caption": "Peugeot 202, Citroën 23, & Renault#NaAGR2", "new_caption": "Peugeot 202, Citroën 23, & Renault AGR2" }, + "38": { + "old_caption": "Cr", + "new_caption": "Crusader II & III Tanks" + }, + "39": { + "old_caption": "M", + "new_caption": "M4, M4A1, & M4A2 Medium Tanks" + }, "40": { "old_caption": "M4A3(75)W, M4A3(76)W, & M4A3(105) Medium Tanks, & M4Tankdozer", "new_caption": "M4A3(75)W, M4A3(76)W, & M4A3(105) Medium Tanks, & M4 Tankdozer" - } + }, + "add": [ + { "vo_note_id": "11", "caption": "D2 & D2(L)", "page_no": 479, "page_pos": [395,715] }, + { "vo_note_id": "15", "caption": "AM Dodge(a)", "page_no": 480, "page_pos": [444,715] } + ] }, "ordnance": { "6": { - "old_caption": "Canon Antichar de 47SA mle 37 APX", + "old_caption": "Canon Antichar de 47", "new_caption": "Canon Antichar de 47 SA mle 37 APX" }, "18": { "old_caption": "Mitrailleuse de 13.2 CAJmle 30", "new_caption": "Mitrailleuse de 13.2 CAJ mle 30" - } + }, + "27": { + "old_caption": "M2A1 & M3 105mm Howitzers: C", + "new_caption": "M2A1 & M3 105mm Howitzers" + }, + "add": [ + { "vo_note_id": "26", "caption": "OQF 25-Pounder Gun-Howitzer", "page_no": 493, "page_pos": [18,715] } + ] } }, @@ -494,7 +348,7 @@ "new_caption": "M3A3(a) FlaK 38" }, "29": { - "old_caption": "Marmon-Herrington III(b) Armored", + "old_caption": "Marmon-Herrington III(b) Armored*2 TK DR", "new_caption": "Marmon-Herrington III(b) Armored Cars" }, "31": { @@ -502,7 +356,7 @@ "new_caption": "L5/30(i) & L3/35(i) & L6/40(i) & M13/40(i)" }, "37": { - "old_caption": "Light Truck & Medium Truck &", + "old_caption": "Light Truck & Medium Truck &Heavy Truck", "new_caption": "Light Truck & Medium Truck & Heavy Truck" } }, @@ -513,7 +367,7 @@ "new_caption": "75M 19S" }, "add": [ - { "vo_note_id": "20", "caption": "3.7cm Infantry Gun", "page_no": 502, "page_pos": [393,616] } + { "vo_note_id": "20", "caption": "3.7cm Infantry Gun", "page_no": 508, "page_pos": [393,616] } ] } }, @@ -555,10 +409,17 @@ "new_vo_note_id": "16", "new_caption": "40M Nimrod" }, + "39": { + "old_caption": "PzKpfw IVD(g), PzKpfw IVF1(g),", + "new_caption": "PzKpfw IVD(g), PzKpfw IVF1(g), & PzKpfw IVH(g)" + }, "50": { "old_caption": "Light Truck, Medium Truck, &Heavy Truck", "new_caption": "Light Truck, Medium Truck, & Heavy Truck" - } + }, + "add": [ + { "vo_note_id": "48", "caption": "RSO(g)", "page_no": 521, "page_pos": [394,713] } + ] }, "ordnance": { "20": { @@ -689,7 +550,10 @@ "old_caption": "ItK/31(r)", "new_vo_note_id": "39", "new_caption": "76 ItK/31(r)" - } + }, + "add": [ + { "vo_note_id": "24", "caption": "105 H/37", "page_no": 553, "page_pos": [394,713] } + ] } }, @@ -708,7 +572,7 @@ "new_caption": "M4A3E8(a) Medium Tank & M4A3E8 Dozer(a)" }, "47": { - "old_caption": "Oxford Carrier, MMG & Oxford Car-rier, HMG", + "old_caption": "Oxford Carrier, MMG & Oxford Car-", "new_caption": "Oxford Carrier, MMG & Oxford Carrier, HMG" }, "57": { diff --git a/asl_rulebook2/extract/index.py b/asl_rulebook2/extract/index.py index 58bce5a..1767371 100755 --- a/asl_rulebook2/extract/index.py +++ b/asl_rulebook2/extract/index.py @@ -15,7 +15,7 @@ from asl_rulebook2.utils import parse_page_numbers, fixup_text, extract_parens_c # --------------------------------------------------------------------- _DEFAULT_ARGS = { - "pages": "10-41", + "pages": "10-42", "index_vp_left": 0, "index_vp_right": 565, "index_vp_top": 715, "index_vp_bottom": 20, # viewport "first_title": "a", "last_title": "X#", # first/last index entries } diff --git a/asl_rulebook2/pdf.py b/asl_rulebook2/pdf.py index 0cdb1a7..35afb7c 100644 --- a/asl_rulebook2/pdf.py +++ b/asl_rulebook2/pdf.py @@ -111,7 +111,7 @@ class PageIterator: class PageElemIterator: """Iterate over each element in a page.""" - def __init__( self, lt_page, elem_filter=None, sort_elems=False ): + def __init__( self, lt_page, elem_filter=None, sort_elems=False, centre_adjust=0 ): self.lt_page = lt_page # collect all the elements (so that they can be sorted) self._elems = [] @@ -127,7 +127,7 @@ class PageElemIterator: walk( lt_page, 0 ) if sort_elems: def sort_key( elem ): - col_no = 0 if elem[1].x0 < lt_page.width/2 else 1 + col_no = 0 if elem[1].x0 < lt_page.width/2 + centre_adjust else 1 # NOTE: Some elements that should be aligned are actually misaligned by a miniscule amount (e.g. 10^-5), # so to stop this from resulting in the wrong sort order, we truncate the decimal places. # NOTE: Characters are often rendered in different fonts, with bounding boxes that don't align neatly. diff --git a/asl_rulebook2/webapp/static/prepare.js b/asl_rulebook2/webapp/static/prepare.js index a9d9fd3..9e45c15 100644 --- a/asl_rulebook2/webapp/static/prepare.js +++ b/asl_rulebook2/webapp/static/prepare.js @@ -140,7 +140,7 @@ gPrepareApp.component( "upload-panel", { Click on the button, and select your copy of MMP's eASLRB.

You must use the offical MMP eASLRB.
A scan of a printed rulebook will not work! -

You should use v1.07 of the eASLRB PDF (normal version, not the "inherited zoom" version). Other versions may work, but may have warnings and/or errors.

+

You should use v2.01 of the eASLRB PDF (normal version, not the "inherited zoom" version). Other versions may work, but may have warnings and/or errors.