use clap::{crate_description, crate_name, crate_version};
use clap::{Parser, ValueHint};
use leptess::Variable;
use std::path::PathBuf;
use thiserror::Error;

#[derive(Error, Debug)]
enum Error {
    #[error("no `=` in key-value pair {value}")]
    ParseKeyValuePair { value: String },

    #[error("invalid tesseract variable name: {value}")]
    TesseractVariableName { value: String },
}

/// Handle application parameter from cli with Clap.
#[derive(Parser, Debug)]
#[clap(name = crate_name!(), about = crate_description!(), version = crate_version!())]
pub struct Opt {
    /// Threshold for subtitle image binarization.
    ///
    /// Must be between 0.0 and 1.0. Only pixels with luminance above the
    /// threshold will be considered text pixels for OCR.
    #[clap(short = 't', long, default_value = "0.6")]
    pub threshold: f32,

    /// DPI of subtitle images.
    ///
    /// This setting doesn't strictly make sense for DVD subtitles, but it can
    /// influence Tesseract's output.
    #[clap(short = 'd', long, default_value = "150")]
    pub dpi: i32,

    /// Border in pixels to surround the each subtitle image for OCR.
    ///
    /// This can have subtle effects on the quality of the OCR.
    #[clap(short = 'b', long, default_value = "10")]
    pub border: u32,

    /// Output subtitle file; stdout if not present.
    #[clap(short = 'o', long, value_parser, value_hint = ValueHint::FilePath)]
    pub output: Option<PathBuf>,

    /// Path to Tesseract's tessdata directory.
    #[clap(short = 'D', long, value_hint = ValueHint::DirPath)]
    pub tessdata_dir: Option<String>,

    /// The Tesseract language(s) to use for OCR.
    #[clap(short = 'l', long)]
    pub lang: String,

    #[expect(clippy::doc_markdown)]
    /// Set values for config variables.
    ///
    /// This works like the `tesseract` command's `-c` argument. One
    /// particularly useful option is `tessedit_char_blacklist=|\/`_~` or
    /// similar, to prevent the OCR from misidentifying characters as symbols
    /// rarely used in subtitles.
    #[clap(short = 'c', long, value_parser = parse_key_val, number_of_values = 1)]
    pub config: Vec<(Variable, String)>,

    /// Set the path of the file to process.
    #[clap(name = "FILE", value_parser, value_hint = ValueHint::FilePath)]
    pub input: PathBuf,

    /// Dump processed subtitle images into the working directory as PNG files.
    #[clap(long)]
    pub dump: bool,

    /// Dump raw subtitle images into the working directory as PNG files.
    #[clap(long)]
    pub dump_raw: bool,
}

// https://github.com/clap-rs/clap_derive/blob/master/examples/keyvalue.rs
fn parse_key_val(s: &str) -> Result<(Variable, String), Error> {
    let pos = s.find('=').ok_or_else(|| Error::ParseKeyValuePair {
        value: s.to_owned(),
    })?;
    Ok((
        parse_tesseract_variable(&s[..pos])?,
        s[pos + 1..].to_owned(),
    ))
}

#[expect(clippy::too_many_lines)]
fn parse_tesseract_variable(s: impl AsRef<str>) -> Result<Variable, Error> {
    Ok(match s.as_ref() {
        "classify_num_cp_levels" => Variable::ClassifyNumCpLevels,
        "textord_dotmatrix_gap" => Variable::TextordDotmatrixGap,
        "textord_debug_block" => Variable::TextordDebugBlock,
        "textord_pitch_range" => Variable::TextordPitchRange,
        "textord_words_veto_power" => Variable::TextordWordsVetoPower,
        "textord_tabfind_show_strokewidths" => Variable::TextordTabfindShowStrokewidths,
        "pitsync_linear_version" => Variable::PitsyncLinearVersion,
        "pitsync_fake_depth" => Variable::PitsyncFakeDepth,
        "oldbl_holed_losscount" => Variable::OldblHoledLosscount,
        "textord_skewsmooth_offset" => Variable::TextordSkewsmoothOffset,
        "textord_skewsmooth_offset2" => Variable::TextordSkewsmoothOffset2,
        "textord_test_x" => Variable::TextordTestX,
        "textord_test_y" => Variable::TextordTestY,
        "textord_min_blobs_in_row" => Variable::TextordMinBlobsInRow,
        "textord_spline_minblobs" => Variable::TextordSplineMinblobs,
        "textord_spline_medianwin" => Variable::TextordSplineMedianwin,
        "textord_max_blob_overlaps" => Variable::TextordMaxBlobOverlaps,
        "textord_min_xheight" => Variable::TextordMinXheight,
        "textord_lms_line_trials" => Variable::TextordLmsLineTrials,
        "textord_tabfind_show_images" => Variable::TextordTabfindShowImages,
        "textord_fp_chop_error" => Variable::TextordFpChopError,
        "edges_max_children_per_outline" => Variable::EdgesMaxChildrenPerOutline,
        "edges_max_children_layers" => Variable::EdgesMaxChildrenLayers,
        "edges_children_per_grandchild" => Variable::EdgesChildrenPerGrandchild,
        "edges_children_count_limit" => Variable::EdgesChildrenCountLimit,
        "edges_min_nonhole" => Variable::EdgesMinNonhole,
        "edges_patharea_ratio" => Variable::EdgesPathareaRatio,
        "devanagari_split_debuglevel" => Variable::DevanagariSplitDebuglevel,
        "textord_tabfind_show_partitions" => Variable::TextordTabfindShowPartitions,
        "textord_debug_tabfind" => Variable::TextordDebugTabfind,
        "textord_debug_bugs" => Variable::TextordDebugBugs,
        "textord_testregion_left" => Variable::TextordTestregionLeft,
        "textord_testregion_top" => Variable::TextordTestregionTop,
        "textord_testregion_right" => Variable::TextordTestregionRight,
        "textord_testregion_bottom" => Variable::TextordTestregionBottom,
        "editor_image_xpos" => Variable::EditorImageXpos,
        "editor_image_ypos" => Variable::EditorImageYpos,
        "editor_image_menuheight" => Variable::EditorImageMenuheight,
        "editor_image_word_bb_color" => Variable::EditorImageWordBbColor,
        "editor_image_blob_bb_color" => Variable::EditorImageBlobBbColor,
        "editor_image_text_color" => Variable::EditorImageTextColor,
        "editor_dbwin_xpos" => Variable::EditorDbwinXpos,
        "editor_dbwin_ypos" => Variable::EditorDbwinYpos,
        "editor_dbwin_height" => Variable::EditorDbwinHeight,
        "editor_dbwin_width" => Variable::EditorDbwinWidth,
        "editor_word_xpos" => Variable::EditorWordXpos,
        "editor_word_ypos" => Variable::EditorWordYpos,
        "editor_word_height" => Variable::EditorWordHeight,
        "editor_word_width" => Variable::EditorWordWidth,
        "wordrec_display_splits" => Variable::WordrecDisplaySplits,
        "poly_debug" => Variable::PolyDebug,
        "poly_wide_objects_better" => Variable::PolyWideObjectsBetter,
        "wordrec_display_all_blobs" => Variable::WordrecDisplayAllBlobs,
        "wordrec_blob_pause" => Variable::WordrecBlobPause,
        "textord_fp_chopping" => Variable::TextordFpChopping,
        "textord_force_make_prop_words" => Variable::TextordForceMakePropWords,
        "textord_chopper_test" => Variable::TextordChopperTest,
        "textord_restore_underlines" => Variable::TextordRestoreUnderlines,
        "textord_show_initial_words" => Variable::TextordShowInitialWords,
        "textord_show_new_words" => Variable::TextordShowNewWords,
        "textord_show_fixed_words" => Variable::TextordShowFixedWords,
        "textord_blocksall_fixed" => Variable::TextordBlocksallFixed,
        "textord_blocksall_prop" => Variable::TextordBlocksallProp,
        "textord_blocksall_testing" => Variable::TextordBlocksallTesting,
        "textord_test_mode" => Variable::TextordTestMode,
        "textord_pitch_scalebigwords" => Variable::TextordPitchScalebigwords,
        "textord_all_prop" => Variable::TextordAllProp,
        "textord_debug_pitch_test" => Variable::TextordDebugPitchTest,
        "textord_disable_pitch_test" => Variable::TextordDisablePitchTest,
        "textord_fast_pitch_test" => Variable::TextordFastPitchTest,
        "textord_debug_pitch_metric" => Variable::TextordDebugPitchMetric,
        "textord_show_row_cuts" => Variable::TextordShowRowCuts,
        "textord_show_page_cuts" => Variable::TextordShowPageCuts,
        "textord_pitch_cheat" => Variable::TextordPitchCheat,
        "textord_blockndoc_fixed" => Variable::TextordBlockndocFixed,
        "textord_show_tables" => Variable::TextordShowTables,
        "textord_tablefind_show_mark" => Variable::TextordTablefindShowMark,
        "textord_tablefind_show_stats" => Variable::TextordTablefindShowStats,
        "textord_tablefind_recognize_tables" => Variable::TextordTablefindRecognizeTables,
        "textord_tabfind_show_initialtabs" => Variable::TextordTabfindShowInitialtabs,
        "textord_tabfind_show_finaltabs" => Variable::TextordTabfindShowFinaltabs,
        "textord_tabfind_only_strokewidths" => Variable::TextordTabfindOnlyStrokewidths,
        "textord_really_old_xheight" => Variable::TextordReallyOldXheight,
        "textord_oldbl_debug" => Variable::TextordOldblDebug,
        "textord_debug_baselines" => Variable::TextordDebugBaselines,
        "textord_oldbl_paradef" => Variable::TextordOldblParadef,
        "textord_oldbl_split_splines" => Variable::TextordOldblSplitSplines,
        "textord_oldbl_merge_parts" => Variable::TextordOldblMergeParts,
        "oldbl_corrfix" => Variable::OldblCorrfix,
        "oldbl_xhfix" => Variable::OldblXhfix,
        "textord_ocropus_mode" => Variable::TextordOcropusMode,
        "textord_heavy_nr" => Variable::TextordHeavyNr,
        "textord_show_initial_rows" => Variable::TextordShowInitialRows,
        "textord_show_parallel_rows" => Variable::TextordShowParallelRows,
        "textord_show_expanded_rows" => Variable::TextordShowExpandedRows,
        "textord_show_final_rows" => Variable::TextordShowFinalRows,
        "textord_show_final_blobs" => Variable::TextordShowFinalBlobs,
        "textord_test_landscape" => Variable::TextordTestLandscape,
        "textord_parallel_baselines" => Variable::TextordParallelBaselines,
        "textord_straight_baselines" => Variable::TextordStraightBaselines,
        "textord_old_baselines" => Variable::TextordOldBaselines,
        "textord_old_xheight" => Variable::TextordOldXheight,
        "textord_fix_xheight_bug" => Variable::TextordFixXheightBug,
        "textord_fix_makerow_bug" => Variable::TextordFixMakerowBug,
        "textord_debug_xheights" => Variable::TextordDebugXheights,
        "textord_biased_skewcalc" => Variable::TextordBiasedSkewcalc,
        "textord_interpolating_skew" => Variable::TextordInterpolatingSkew,
        "textord_new_initial_xheight" => Variable::TextordNewInitialXheight,
        "textord_debug_blob" => Variable::TextordDebugBlob,
        "gapmap_debug" => Variable::GapmapDebug,
        "gapmap_use_ends" => Variable::GapmapUseEnds,
        "gapmap_no_isolated_quanta" => Variable::GapmapNoIsolatedQuanta,
        "edges_use_new_outline_complexity" => Variable::EdgesUseNewOutlineComplexity,
        "edges_debug" => Variable::EdgesDebug,
        "edges_children_fix" => Variable::EdgesChildrenFix,
        "textord_show_fixed_cuts" => Variable::TextordShowFixedCuts,
        "devanagari_split_debugimage" => Variable::DevanagariSplitDebugimage,
        "textord_tabfind_show_initial_partitions" => Variable::TextordTabfindShowInitialPartitions,
        "textord_tabfind_show_reject_blobs" => Variable::TextordTabfindShowRejectBlobs,
        "textord_tabfind_show_columns" => Variable::TextordTabfindShowColumns,
        "textord_tabfind_show_blocks" => Variable::TextordTabfindShowBlocks,
        "textord_tabfind_find_tables" => Variable::TextordTabfindFindTables,
        "textord_space_size_is_variable" => Variable::TextordSpaceSizeIsVariable,
        "textord_debug_printable" => Variable::TextordDebugPrintable,
        "equationdetect_save_bi_image" => Variable::EquationdetectSaveBiImage,
        "equationdetect_save_spt_image" => Variable::EquationdetectSaveSptImage,
        "equationdetect_save_seed_image" => Variable::EquationdetectSaveSeedImage,
        "equationdetect_save_merged_image" => Variable::EquationdetectSaveMergedImage,
        "stream_filelist" => Variable::StreamFilelist,
        "debug_file" => Variable::DebugFile,
        "dotproduct" => Variable::Dotproduct,
        "classify_font_name" => Variable::ClassifyFontName,
        "fx_debugfile" => Variable::FxDebugfile,
        "editor_image_win_name" => Variable::EditorImageWinName,
        "editor_dbwin_name" => Variable::EditorDbwinName,
        "editor_word_name" => Variable::EditorWordName,
        "document_title" => Variable::DocumentTitle,
        "classify_pico_feature_length" => Variable::ClassifyPicoFeatureLength,
        "classify_norm_adj_midpoint" => Variable::ClassifyNormAdjMidpoint,
        "classify_norm_adj_curl" => Variable::ClassifyNormAdjCurl,
        "classify_min_slope" => Variable::ClassifyMinSlope,
        "classify_max_slope" => Variable::ClassifyMaxSlope,
        "classify_cp_angle_pad_loose" => Variable::ClassifyCpAnglePadLoose,
        "classify_cp_angle_pad_medium" => Variable::ClassifyCpAnglePadMedium,
        "classify_cp_angle_pad_tight" => Variable::ClassifyCpAnglePadTight,
        "classify_cp_end_pad_loose" => Variable::ClassifyCpEndPadLoose,
        "classify_cp_end_pad_medium" => Variable::ClassifyCpEndPadMedium,
        "classify_cp_end_pad_tight" => Variable::ClassifyCpEndPadTight,
        "classify_cp_side_pad_loose" => Variable::ClassifyCpSidePadLoose,
        "classify_cp_side_pad_medium" => Variable::ClassifyCpSidePadMedium,
        "classify_cp_side_pad_tight" => Variable::ClassifyCpSidePadTight,
        "classify_pp_angle_pad" => Variable::ClassifyPpAnglePad,
        "classify_pp_end_pad" => Variable::ClassifyPpEndPad,
        "classify_pp_side_pad" => Variable::ClassifyPpSidePad,
        "textord_underline_offset" => Variable::TextordUnderlineOffset,
        "textord_wordstats_smooth_factor" => Variable::TextordWordstatsSmoothFactor,
        "textord_width_smooth_factor" => Variable::TextordWidthSmoothFactor,
        "textord_words_width_ile" => Variable::TextordWordsWidthIle,
        "textord_words_maxspace" => Variable::TextordWordsMaxspace,
        "textord_words_default_maxspace" => Variable::TextordWordsDefaultMaxspace,
        "textord_words_default_minspace" => Variable::TextordWordsDefaultMinspace,
        "textord_words_min_minspace" => Variable::TextordWordsMinMinspace,
        "textord_words_default_nonspace" => Variable::TextordWordsDefaultNonspace,
        "textord_words_initial_lower" => Variable::TextordWordsInitialLower,
        "textord_words_initial_upper" => Variable::TextordWordsInitialUpper,
        "textord_words_minlarge" => Variable::TextordWordsMinlarge,
        "textord_words_pitchsd_threshold" => Variable::TextordWordsPitchsdThreshold,
        "textord_words_def_fixed" => Variable::TextordWordsDefFixed,
        "textord_words_def_prop" => Variable::TextordWordsDefProp,
        "textord_pitch_rowsimilarity" => Variable::TextordPitchRowsimilarity,
        "words_initial_lower" => Variable::WordsInitialLower,
        "words_initial_upper" => Variable::WordsInitialUpper,
        "words_default_prop_nonspace" => Variable::WordsDefaultPropNonspace,
        "words_default_fixed_space" => Variable::WordsDefaultFixedSpace,
        "words_default_fixed_limit" => Variable::WordsDefaultFixedLimit,
        "textord_words_definite_spread" => Variable::TextordWordsDefiniteSpread,
        "textord_spacesize_ratiofp" => Variable::TextordSpacesizeRatiofp,
        "textord_spacesize_ratioprop" => Variable::TextordSpacesizeRatioprop,
        "textord_fpiqr_ratio" => Variable::TextordFpiqrRatio,
        "textord_max_pitch_iqr" => Variable::TextordMaxPitchIqr,
        "textord_fp_min_width" => Variable::TextordFpMinWidth,
        "textord_projection_scale" => Variable::TextordProjectionScale,
        "textord_balance_factor" => Variable::TextordBalanceFactor,
        "textord_tabvector_vertical_gap_fraction" => Variable::TextordTabvectorVerticalGapFraction,
        "textord_tabvector_vertical_box_ratio" => Variable::TextordTabvectorVerticalBoxRatio,
        "pitsync_joined_edge" => Variable::PitsyncJoinedEdge,
        "pitsync_offset_freecut_fraction" => Variable::PitsyncOffsetFreecutFraction,
        "oldbl_xhfract" => Variable::OldblXhfract,
        "oldbl_dot_error_size" => Variable::OldblDotErrorSize,
        "textord_oldbl_jumplimit" => Variable::TextordOldblJumplimit,
        "textord_spline_shift_fraction" => Variable::TextordSplineShiftFraction,
        "textord_spline_outlier_fraction" => Variable::TextordSplineOutlierFraction,
        "textord_skew_ile" => Variable::TextordSkewIle,
        "textord_skew_lag" => Variable::TextordSkewLag,
        "textord_linespace_iqrlimit" => Variable::TextordLinespaceIqrlimit,
        "textord_width_limit" => Variable::TextordWidthLimit,
        "textord_chop_width" => Variable::TextordChopWidth,
        "textord_expansion_factor" => Variable::TextordExpansionFactor,
        "textord_overlap_x" => Variable::TextordOverlapX,
        "textord_minxh" => Variable::TextordMinxh,
        "textord_min_linesize" => Variable::TextordMinLinesize,
        "textord_excess_blobsize" => Variable::TextordExcessBlobsize,
        "textord_occupancy_threshold" => Variable::TextordOccupancyThreshold,
        "textord_underline_width" => Variable::TextordUnderlineWidth,
        "textord_min_blob_height_fraction" => Variable::TextordMinBlobHeightFraction,
        "textord_xheight_mode_fraction" => Variable::TextordXheightModeFraction,
        "textord_ascheight_mode_fraction" => Variable::TextordAscheightModeFraction,
        "textord_descheight_mode_fraction" => Variable::TextordDescheightModeFraction,
        "textord_ascx_ratio_min" => Variable::TextordAscxRatioMin,
        "textord_ascx_ratio_max" => Variable::TextordAscxRatioMax,
        "textord_descx_ratio_min" => Variable::TextordDescxRatioMin,
        "textord_descx_ratio_max" => Variable::TextordDescxRatioMax,
        "textord_xheight_error_margin" => Variable::TextordXheightErrorMargin,
        "gapmap_big_gaps" => Variable::GapmapBigGaps,
        "textord_fp_chop_snap" => Variable::TextordFpChopSnap,
        "edges_childarea" => Variable::EdgesChildarea,
        "edges_boxarea" => Variable::EdgesBoxarea,
        "textord_underline_threshold" => Variable::TextordUnderlineThreshold,
        "ambigs_debug_level" => Variable::AmbigsDebugLevel,
        "classify_debug_level" => Variable::ClassifyDebugLevel,
        "classify_norm_method" => Variable::ClassifyNormMethod,
        "matcher_debug_level" => Variable::MatcherDebugLevel,
        "matcher_debug_flags" => Variable::MatcherDebugFlags,
        "classify_learning_debug_level" => Variable::ClassifyLearningDebugLevel,
        "matcher_permanent_classes_min" => Variable::MatcherPermanentClassesMin,
        "matcher_min_examples_for_prototyping" => Variable::MatcherMinExamplesForPrototyping,
        "matcher_sufficient_examples_for_prototyping" => {
            Variable::MatcherSufficientExamplesForPrototyping
        }
        "classify_adapt_proto_threshold" => Variable::ClassifyAdaptProtoThreshold,
        "classify_adapt_feature_threshold" => Variable::ClassifyAdaptFeatureThreshold,
        "classify_class_pruner_threshold" => Variable::ClassifyClassPrunerThreshold,
        "classify_class_pruner_multiplier" => Variable::ClassifyClassPrunerMultiplier,
        "classify_cp_cutoff_strength" => Variable::ClassifyCpCutoffStrength,
        "classify_integer_matcher_multiplier" => Variable::ClassifyIntegerMatcherMultiplier,
        "dawg_debug_level" => Variable::DawgDebugLevel,
        "hyphen_debug_level" => Variable::HyphenDebugLevel,
        "stopper_smallword_size" => Variable::StopperSmallwordSize,
        "stopper_debug_level" => Variable::StopperDebugLevel,
        "tessedit_truncate_wordchoice_log" => Variable::TesseditTruncateWordchoiceLog,
        "max_permuter_attempts" => Variable::MaxPermuterAttempts,
        "repair_unchopped_blobs" => Variable::RepairUnchoppedBlobs,
        "chop_debug" => Variable::ChopDebug,
        "chop_split_length" => Variable::ChopSplitLength,
        "chop_same_distance" => Variable::ChopSameDistance,
        "chop_min_outline_points" => Variable::ChopMinOutlinePoints,
        "chop_seam_pile_size" => Variable::ChopSeamPileSize,
        "chop_inside_angle" => Variable::ChopInsideAngle,
        "chop_min_outline_area" => Variable::ChopMinOutlineArea,
        "chop_centered_maxwidth" => Variable::ChopCenteredMaxwidth,
        "chop_x_y_weight" => Variable::ChopXyWeight,
        "wordrec_debug_level" => Variable::WordrecDebugLevel,
        "wordrec_max_join_chunks" => Variable::WordrecMaxJoinChunks,
        "segsearch_debug_level" => Variable::SegsearchDebugLevel,
        "segsearch_max_pain_points" => Variable::SegsearchMaxPainPoints,
        "segsearch_max_futile_classifications" => Variable::SegsearchMaxFutileClassifications,
        "language_model_debug_level" => Variable::LanguageModelDebugLevel,
        "language_model_ngram_order" => Variable::LanguageModelNgramOrder,
        "language_model_viterbi_list_max_num_prunable" => {
            Variable::LanguageModelViterbiListMaxNumPrunable
        }
        "language_model_viterbi_list_max_size" => Variable::LanguageModelViterbiListMaxSize,
        "language_model_min_compound_length" => Variable::LanguageModelMinCompoundLength,
        "wordrec_display_segmentations" => Variable::WordrecDisplaySegmentations,
        "tessedit_pageseg_mode" => Variable::TesseditPagesegMode,
        "tessedit_ocr_engine_mode" => Variable::TesseditOcrEngineMode,
        "pageseg_devanagari_split_strategy" => Variable::PagesegDevanagariSplitStrategy,
        "ocr_devanagari_split_strategy" => Variable::OcrDevanagariSplitStrategy,
        "bidi_debug" => Variable::BidiDebug,
        "applybox_debug" => Variable::ApplyboxDebug,
        "applybox_page" => Variable::ApplyboxPage,
        "tessedit_bigram_debug" => Variable::TesseditBigramDebug,
        "debug_noise_removal" => Variable::DebugNoiseRemoval,
        "noise_maxperblob" => Variable::NoiseMaxperblob,
        "noise_maxperword" => Variable::NoiseMaxperword,
        "debug_x_ht_level" => Variable::DebugXHtLevel,
        "quality_min_initial_alphas_reqd" => Variable::QualityMinInitialAlphasReqd,
        "tessedit_tess_adaption_mode" => Variable::TesseditTessAdaptionMode,
        "multilang_debug_level" => Variable::MultilangDebugLevel,
        "paragraph_debug_level" => Variable::ParagraphDebugLevel,
        "tessedit_preserve_min_wd_len" => Variable::TesseditPreserveMinWdLen,
        "crunch_rating_max" => Variable::CrunchRatingMax,
        "crunch_pot_indicators" => Variable::CrunchPotIndicators,
        "crunch_leave_lc_strings" => Variable::CrunchLeaveLcStrings,
        "crunch_leave_uc_strings" => Variable::CrunchLeaveUcStrings,
        "crunch_long_repetitions" => Variable::CrunchLongRepetitions,
        "crunch_debug" => Variable::CrunchDebug,
        "fixsp_non_noise_limit" => Variable::FixspNonNoiseLimit,
        "fixsp_done_mode" => Variable::FixspDoneMode,
        "debug_fix_space_level" => Variable::DebugFixSpaceLevel,
        "x_ht_acceptance_tolerance" => Variable::XHtAcceptanceTolerance,
        "x_ht_min_change" => Variable::XHtMinChange,
        "superscript_debug" => Variable::SuperscriptDebug,
        "jpg_quality" => Variable::JpgQuality,
        "user_defined_dpi" => Variable::UserDefinedDpi,
        "min_characters_to_try" => Variable::MinCharactersToTry,
        "suspect_level" => Variable::SuspectLevel,
        "suspect_short_words" => Variable::SuspectShortWords,
        "tessedit_reject_mode" => Variable::TesseditRejectMode,
        "tessedit_image_border" => Variable::TesseditImageBorder,
        "min_sane_x_ht_pixels" => Variable::MinSaneXHtPixels,
        "tessedit_page_number" => Variable::TesseditPageNumber,
        "tessedit_parallelize" => Variable::TesseditParallelize,
        "lstm_choice_mode" => Variable::LstmChoiceMode,
        "tosp_debug_level" => Variable::TospDebugLevel,
        "tosp_enough_space_samples_for_median" => Variable::TospEnoughSpaceSamplesForMedian,
        "tosp_redo_kern_limit" => Variable::TospRedoKernLimit,
        "tosp_few_samples" => Variable::TospFewSamples,
        "tosp_short_row" => Variable::TospShortRow,
        "tosp_sanity_method" => Variable::TospSanityMethod,
        "textord_max_noise_size" => Variable::TextordMaxNoiseSize,
        "textord_baseline_debug" => Variable::TextordBaselineDebug,
        "textord_noise_sizefraction" => Variable::TextordNoiseSizefraction,
        "textord_noise_translimit" => Variable::TextordNoiseTranslimit,
        "textord_noise_sncount" => Variable::TextordNoiseSncount,
        "use_ambigs_for_adaption" => Variable::UseAmbigsForAdaption,
        "allow_blob_division" => Variable::AllowBlobDivision,
        "prioritize_division" => Variable::PrioritizeDivision,
        "classify_enable_learning" => Variable::ClassifyEnableLearning,
        "tess_cn_matching" => Variable::TessCnMatching,
        "tess_bn_matching" => Variable::TessBnMatching,
        "classify_enable_adaptive_matcher" => Variable::ClassifyEnableAdaptiveMatcher,
        "classify_use_pre_adapted_templates" => Variable::ClassifyUsePreAdaptedTemplates,
        "classify_save_adapted_templates" => Variable::ClassifySaveAdaptedTemplates,
        "classify_enable_adaptive_debugger" => Variable::ClassifyEnableAdaptiveDebugger,
        "classify_nonlinear_norm" => Variable::ClassifyNonlinearNorm,
        "disable_character_fragments" => Variable::DisableCharacterFragments,
        "classify_debug_character_fragments" => Variable::ClassifyDebugCharacterFragments,
        "matcher_debug_separate_windows" => Variable::MatcherDebugSeparateWindows,
        "classify_bln_numeric_mode" => Variable::ClassifyBlnNumericMode,
        "load_system_dawg" => Variable::LoadSystemDawg,
        "load_freq_dawg" => Variable::LoadFreqDawg,
        "load_unambig_dawg" => Variable::LoadUnambigDawg,
        "load_punc_dawg" => Variable::LoadPuncDawg,
        "load_number_dawg" => Variable::LoadNumberDawg,
        "load_bigram_dawg" => Variable::LoadBigramDawg,
        "use_only_first_uft8_step" => Variable::UseOnlyFirstUft8Step,
        "stopper_no_acceptable_choices" => Variable::StopperNoAcceptableChoices,
        "segment_nonalphabetic_script" => Variable::SegmentNonalphabeticScript,
        "save_doc_words" => Variable::SaveDocWords,
        "merge_fragments_in_matrix" => Variable::MergeFragmentsInMatrix,
        "wordrec_enable_assoc" => Variable::WordrecEnableAssoc,
        "force_word_assoc" => Variable::ForceWordAssoc,
        "chop_enable" => Variable::ChopEnable,
        "chop_vertical_creep" => Variable::ChopVerticalCreep,
        "chop_new_seam_pile" => Variable::ChopNewSeamPile,
        "assume_fixed_pitch_char_segment" => Variable::AssumeFixedPitchCharSegment,
        "wordrec_skip_no_truth_words" => Variable::WordrecSkipNoTruthWords,
        "wordrec_debug_blamer" => Variable::WordrecDebugBlamer,
        "wordrec_run_blamer" => Variable::WordrecRunBlamer,
        "save_alt_choices" => Variable::SaveAltChoices,
        "language_model_ngram_on" => Variable::LanguageModelNgramOn,
        "language_model_ngram_use_only_first_uft8_step" => {
            Variable::LanguageModelNgramUseOnlyFirstUft8Step
        }
        "language_model_ngram_space_delimited_language" => {
            Variable::LanguageModelNgramSpaceDelimitedLanguage
        }
        "language_model_use_sigmoidal_certainty" => Variable::LanguageModelUseSigmoidalCertainty,
        "tessedit_resegment_from_boxes" => Variable::TesseditResegmentFromBoxes,
        "tessedit_resegment_from_line_boxes" => Variable::TesseditResegmentFromLineBoxes,
        "tessedit_train_from_boxes" => Variable::TesseditTrainFromBoxes,
        "tessedit_make_boxes_from_boxes" => Variable::TesseditMakeBoxesFromBoxes,
        "tessedit_train_line_recognizer" => Variable::TesseditTrainLineRecognizer,
        "tessedit_dump_pageseg_images" => Variable::TesseditDumpPagesegImages,
        "tessedit_do_invert" => Variable::TesseditDoInvert,
        "tessedit_ambigs_training" => Variable::TesseditAmbigsTraining,
        "tessedit_adaption_debug" => Variable::TesseditAdaptionDebug,
        "applybox_learn_chars_and_char_frags_mode" => Variable::ApplyboxLearnCharsAndCharFragsMode,
        "applybox_learn_ngrams_mode" => Variable::ApplyboxLearnNgramsMode,
        "tessedit_display_outwords" => Variable::TesseditDisplayOutwords,
        "tessedit_dump_choices" => Variable::TesseditDumpChoices,
        "tessedit_timing_debug" => Variable::TesseditTimingDebug,
        "tessedit_fix_fuzzy_spaces" => Variable::TesseditFixFuzzySpaces,
        "tessedit_unrej_any_wd" => Variable::TesseditUnrejAnyWd,
        "tessedit_fix_hyphens" => Variable::TesseditFixHyphens,
        "tessedit_enable_doc_dict" => Variable::TesseditEnableDocDict,
        "tessedit_debug_fonts" => Variable::TesseditDebugFonts,
        "tessedit_debug_block_rejection" => Variable::TesseditDebugBlockRejection,
        "tessedit_enable_bigram_correction" => Variable::TesseditEnableBigramCorrection,
        "tessedit_enable_dict_correction" => Variable::TesseditEnableDictCorrection,
        "enable_noise_removal" => Variable::EnableNoiseRemoval,
        "tessedit_minimal_rej_pass1" => Variable::TesseditMinimalRejPass1,
        "tessedit_test_adaption" => Variable::TesseditTestAdaption,
        "test_pt" => Variable::TestPt,
        "paragraph_text_based" => Variable::ParagraphTextBased,
        "lstm_use_matrix" => Variable::LstmUseMatrix,
        "tessedit_good_quality_unrej" => Variable::TesseditGoodQualityUnrej,
        "tessedit_use_reject_spaces" => Variable::TesseditUseRejectSpaces,
        "tessedit_preserve_blk_rej_perfect_wds" => Variable::TesseditPreserveBlkRejPerfectWds,
        "tessedit_preserve_row_rej_perfect_wds" => Variable::TesseditPreserveRowRejPerfectWds,
        "tessedit_dont_blkrej_good_wds" => Variable::TesseditDontBlkrejGoodWds,
        "tessedit_dont_rowrej_good_wds" => Variable::TesseditDontRowrejGoodWds,
        "tessedit_row_rej_good_docs" => Variable::TesseditRowRejGoodDocs,
        "tessedit_reject_bad_qual_wds" => Variable::TesseditRejectBadQualWds,
        "tessedit_debug_doc_rejection" => Variable::TesseditDebugDocRejection,
        "tessedit_debug_quality_metrics" => Variable::TesseditDebugQualityMetrics,
        "bland_unrej" => Variable::BlandUnrej,
        "unlv_tilde_crunching" => Variable::UnlvTildeCrunching,
        "hocr_font_info" => Variable::HocrFontInfo,
        "hocr_char_boxes" => Variable::HocrCharBoxes,
        "crunch_early_merge_tess_fails" => Variable::CrunchEarlyMergeTessFails,
        "crunch_early_convert_bad_unlv_chs" => Variable::CrunchEarlyConvertBadUnlvChs,
        "crunch_terrible_garbage" => Variable::CrunchTerribleGarbage,
        "crunch_leave_ok_strings" => Variable::CrunchLeaveOkStrings,
        "crunch_accept_ok" => Variable::CrunchAcceptOk,
        "crunch_leave_accept_strings" => Variable::CrunchLeaveAcceptStrings,
        "crunch_include_numerals" => Variable::CrunchIncludeNumerals,
        "tessedit_prefer_joined_punct" => Variable::TesseditPreferJoinedPunct,
        "tessedit_write_block_separators" => Variable::TesseditWriteBlockSeparators,
        "tessedit_write_rep_codes" => Variable::TesseditWriteRepCodes,
        "tessedit_write_unlv" => Variable::TesseditWriteUnlv,
        "tessedit_create_txt" => Variable::TesseditCreateTxt,
        "tessedit_create_hocr" => Variable::TesseditCreateHocr,
        "tessedit_create_alto" => Variable::TesseditCreateAlto,
        "tessedit_create_lstmbox" => Variable::TesseditCreateLstmbox,
        "tessedit_create_tsv" => Variable::TesseditCreateTsv,
        "tessedit_create_wordstrbox" => Variable::TesseditCreateWordstrbox,
        "tessedit_create_pdf" => Variable::TesseditCreatePdf,
        "textonly_pdf" => Variable::TextonlyPdf,
        "suspect_constrain_1Il" => Variable::SuspectConstrain1Il,
        "tessedit_minimal_rejection" => Variable::TesseditMinimalRejection,
        "tessedit_zero_rejection" => Variable::TesseditZeroRejection,
        "tessedit_word_for_word" => Variable::TesseditWordForWord,
        "tessedit_zero_kelvin_rejection" => Variable::TesseditZeroKelvinRejection,
        "tessedit_rejection_debug" => Variable::TesseditRejectionDebug,
        "tessedit_flip_0O" => Variable::TesseditFlip0O,
        "rej_trust_doc_dawg" => Variable::RejTrustDocDawg,
        "rej_1Il_use_dict_word" => Variable::Rej1IlUseDictWord,
        "rej_1Il_trust_permuter_type" => Variable::Rej1IlTrustPermuterType,
        "rej_use_tess_accepted" => Variable::RejUseTessAccepted,
        "rej_use_tess_blanks" => Variable::RejUseTessBlanks,
        "rej_use_good_perm" => Variable::RejUseGoodPerm,
        "rej_use_sensible_wd" => Variable::RejUseSensibleWd,
        "rej_alphas_in_number_perm" => Variable::RejAlphasInNumberPerm,
        "tessedit_create_boxfile" => Variable::TesseditCreateBoxfile,
        "tessedit_write_images" => Variable::TesseditWriteImages,
        "interactive_display_mode" => Variable::InteractiveDisplayMode,
        "tessedit_override_permuter" => Variable::TesseditOverridePermuter,
        "tessedit_use_primary_params_model" => Variable::TesseditUsePrimaryParamsModel,
        "textord_tabfind_show_vlines" => Variable::TextordTabfindShowVlines,
        "textord_use_cjk_fp_model" => Variable::TextordUseCjkFpModel,
        "poly_allow_detailed_fx" => Variable::PolyAllowDetailedFx,
        "tessedit_init_config_only" => Variable::TesseditInitConfigOnly,
        "textord_equation_detect" => Variable::TextordEquationDetect,
        "textord_tabfind_vertical_text" => Variable::TextordTabfindVerticalText,
        "textord_tabfind_force_vertical_text" => Variable::TextordTabfindForceVerticalText,
        "preserve_interword_spaces" => Variable::PreserveInterwordSpaces,
        "pageseg_apply_music_mask" => Variable::PagesegApplyMusicMask,
        "textord_single_height_mode" => Variable::TextordSingleHeightMode,
        "tosp_old_to_method" => Variable::TospOldToMethod,
        "tosp_old_to_constrain_sp_kn" => Variable::TospOldToConstrainSpKn,
        "tosp_only_use_prop_rows" => Variable::TospOnlyUsePropRows,
        "tosp_force_wordbreak_on_punct" => Variable::TospForceWordbreakOnPunct,
        "tosp_use_pre_chopping" => Variable::TospUsePreChopping,
        "tosp_old_to_bug_fix" => Variable::TospOldToBugFix,
        "tosp_block_use_cert_spaces" => Variable::TospBlockUseCertSpaces,
        "tosp_row_use_cert_spaces" => Variable::TospRowUseCertSpaces,
        "tosp_narrow_blobs_not_cert" => Variable::TospNarrowBlobsNotCert,
        "tosp_row_use_cert_spaces1" => Variable::TospRowUseCertSpaces1,
        "tosp_recovery_isolated_row_stats" => Variable::TospRecoveryIsolatedRowStats,
        "tosp_only_small_gaps_for_kern" => Variable::TospOnlySmallGapsForKern,
        "tosp_all_flips_fuzzy" => Variable::TospAllFlipsFuzzy,
        "tosp_fuzzy_limit_all" => Variable::TospFuzzyLimitAll,
        "tosp_stats_use_xht_gaps" => Variable::TospStatsUseXhtGaps,
        "tosp_use_xht_gaps" => Variable::TospUseXhtGaps,
        "tosp_only_use_xht_gaps" => Variable::TospOnlyUseXhtGaps,
        "tosp_rule_9_test_punct" => Variable::TospRule9TestPunct,
        "tosp_flip_fuzz_kn_to_sp" => Variable::TospFlipFuzzKnToSp,
        "tosp_flip_fuzz_sp_to_kn" => Variable::TospFlipFuzzSpToKn,
        "tosp_improve_thresh" => Variable::TospImproveThresh,
        "textord_no_rejects" => Variable::TextordNoRejects,
        "textord_show_blobs" => Variable::TextordShowBlobs,
        "textord_show_boxes" => Variable::TextordShowBoxes,
        "textord_noise_rejwords" => Variable::TextordNoiseRejwords,
        "textord_noise_rejrows" => Variable::TextordNoiseRejrows,
        "textord_noise_debug" => Variable::TextordNoiseDebug,
        "classify_learn_debug_str" => Variable::ClassifyLearnDebugStr,
        "user_words_file" => Variable::UserWordsFile,
        "user_words_suffix" => Variable::UserWordsSuffix,
        "user_patterns_file" => Variable::UserPatternsFile,
        "user_patterns_suffix" => Variable::UserPatternsSuffix,
        "output_ambig_words_file" => Variable::OutputAmbigWordsFile,
        "word_to_debug" => Variable::WordToDebug,
        "tessedit_char_blacklist" => Variable::TesseditCharBlacklist,
        "tessedit_char_whitelist" => Variable::TesseditCharWhitelist,
        "tessedit_char_unblacklist" => Variable::TesseditCharUnblacklist,
        "tessedit_write_params_to_file" => Variable::TesseditWriteParamsToFile,
        "applybox_exposure_pattern" => Variable::ApplyboxExposurePattern,
        "chs_leading_punct" => Variable::ChsLeadingPunct,
        "chs_trailing_punct1" => Variable::ChsTrailingPunct1,
        "chs_trailing_punct2" => Variable::ChsTrailingPunct2,
        "outlines_odd" => Variable::OutlinesOdd,
        "outlines_2" => Variable::Outlines2,
        "numeric_punctuation" => Variable::NumericPunctuation,
        "unrecognised_char" => Variable::UnrecognisedChar,
        "ok_repeated_ch_non_alphanum_wds" => Variable::OkRepeatedChNonAlphanumWds,
        "conflict_set_I_l_1" => Variable::ConflictSetIl1,
        "file_type" => Variable::FileType,
        "tessedit_load_sublangs" => Variable::TesseditLoadSublangs,
        "page_separator" => Variable::PageSeparator,
        "classify_char_norm_range" => Variable::ClassifyCharNormRange,
        "classify_max_rating_ratio" => Variable::ClassifyMaxRatingRatio,
        "classify_max_certainty_margin" => Variable::ClassifyMaxCertaintyMargin,
        "matcher_good_threshold" => Variable::MatcherGoodThreshold,
        "matcher_reliable_adaptive_result" => Variable::MatcherReliableAdaptiveResult,
        "matcher_perfect_threshold" => Variable::MatcherPerfectThreshold,
        "matcher_bad_match_pad" => Variable::MatcherBadMatchPad,
        "matcher_rating_margin" => Variable::MatcherRatingMargin,
        "matcher_avg_noise_size" => Variable::MatcherAvgNoiseSize,
        "matcher_clustering_max_angle_delta" => Variable::MatcherClusteringMaxAngleDelta,
        "classify_misfit_junk_penalty" => Variable::ClassifyMisfitJunkPenalty,
        "rating_scale" => Variable::RatingScale,
        "certainty_scale" => Variable::CertaintyScale,
        "tessedit_class_miss_scale" => Variable::TesseditClassMissScale,
        "classify_adapted_pruning_factor" => Variable::ClassifyAdaptedPruningFactor,
        "classify_adapted_pruning_threshold" => Variable::ClassifyAdaptedPruningThreshold,
        "classify_character_fragments_garbage_certainty_threshold" => {
            Variable::ClassifyCharacterFragmentsGarbageCertaintyThreshold
        }
        "speckle_large_max_size" => Variable::SpeckleLargeMaxSize,
        "speckle_rating_penalty" => Variable::SpeckleRatingPenalty,
        "xheight_penalty_subscripts" => Variable::XheightPenaltySubscripts,
        "xheight_penalty_inconsistent" => Variable::XheightPenaltyInconsistent,
        "segment_penalty_dict_frequent_word" => Variable::SegmentPenaltyDictFrequentWord,
        "segment_penalty_dict_case_ok" => Variable::SegmentPenaltyDictCaseOk,
        "segment_penalty_dict_case_bad" => Variable::SegmentPenaltyDictCaseBad,
        "segment_penalty_dict_nonword" => Variable::SegmentPenaltyDictNonword,
        "segment_penalty_garbage" => Variable::SegmentPenaltyGarbage,
        "stopper_nondict_certainty_base" => Variable::StopperNondictCertaintyBase,
        "stopper_phase2_certainty_rejection_offset" => {
            Variable::StopperPhase2CertaintyRejectionOffset
        }
        "stopper_certainty_per_char" => Variable::StopperCertaintyPerChar,
        "stopper_allowable_character_badness" => Variable::StopperAllowableCharacterBadness,
        "doc_dict_pending_threshold" => Variable::DocDictPendingThreshold,
        "doc_dict_certainty_threshold" => Variable::DocDictCertaintyThreshold,
        "tessedit_certainty_threshold" => Variable::TesseditCertaintyThreshold,
        "chop_split_dist_knob" => Variable::ChopSplitDistKnob,
        "chop_overlap_knob" => Variable::ChopOverlapKnob,
        "chop_center_knob" => Variable::ChopCenterKnob,
        "chop_sharpness_knob" => Variable::ChopSharpnessKnob,
        "chop_width_change_knob" => Variable::ChopWidthChangeKnob,
        "chop_ok_split" => Variable::ChopOkSplit,
        "chop_good_split" => Variable::ChopGoodSplit,
        "segsearch_max_char_wh_ratio" => Variable::SegsearchMaxCharWhRatio,
        "language_model_ngram_small_prob" => Variable::LanguageModelNgramSmallProb,
        "language_model_ngram_nonmatch_score" => Variable::LanguageModelNgramNonmatchScore,
        "language_model_ngram_scale_factor" => Variable::LanguageModelNgramScaleFactor,
        "language_model_ngram_rating_factor" => Variable::LanguageModelNgramRatingFactor,
        "language_model_penalty_non_freq_dict_word" => {
            Variable::LanguageModelPenaltyNonFreqDictWord
        }
        "language_model_penalty_non_dict_word" => Variable::LanguageModelPenaltyNonDictWord,
        "language_model_penalty_punc" => Variable::LanguageModelPenaltyPunc,
        "language_model_penalty_case" => Variable::LanguageModelPenaltyCase,
        "language_model_penalty_script" => Variable::LanguageModelPenaltyScript,
        "language_model_penalty_chartype" => Variable::LanguageModelPenaltyChartype,
        "language_model_penalty_font" => Variable::LanguageModelPenaltyFont,
        "language_model_penalty_spacing" => Variable::LanguageModelPenaltySpacing,
        "language_model_penalty_increment" => Variable::LanguageModelPenaltyIncrement,
        "noise_cert_basechar" => Variable::NoiseCertBasechar,
        "noise_cert_disjoint" => Variable::NoiseCertDisjoint,
        "noise_cert_punc" => Variable::NoiseCertPunc,
        "noise_cert_factor" => Variable::NoiseCertFactor,
        "quality_rej_pc" => Variable::QualityRejPc,
        "quality_blob_pc" => Variable::QualityBlobPc,
        "quality_outline_pc" => Variable::QualityOutlinePc,
        "quality_char_pc" => Variable::QualityCharPc,
        "test_pt_x" => Variable::TestPtX,
        "test_pt_y" => Variable::TestPtY,
        "tessedit_reject_doc_percent" => Variable::TesseditRejectDocPercent,
        "tessedit_reject_block_percent" => Variable::TesseditRejectBlockPercent,
        "tessedit_reject_row_percent" => Variable::TesseditRejectRowPercent,
        "tessedit_whole_wd_rej_row_percent" => Variable::TesseditWholeWdRejRowPercent,
        "tessedit_good_doc_still_rowrej_wd" => Variable::TesseditGoodDocStillRowrejWd,
        "quality_rowrej_pc" => Variable::QualityRowrejPc,
        "crunch_terrible_rating" => Variable::CrunchTerribleRating,
        "crunch_poor_garbage_cert" => Variable::CrunchPoorGarbageCert,
        "crunch_poor_garbage_rate" => Variable::CrunchPoorGarbageRate,
        "crunch_pot_poor_rate" => Variable::CrunchPotPoorRate,
        "crunch_pot_poor_cert" => Variable::CrunchPotPoorCert,
        "crunch_del_rating" => Variable::CrunchDelRating,
        "crunch_del_cert" => Variable::CrunchDelCert,
        "crunch_del_min_ht" => Variable::CrunchDelMinHt,
        "crunch_del_max_ht" => Variable::CrunchDelMaxHt,
        "crunch_del_min_width" => Variable::CrunchDelMinWidth,
        "crunch_del_high_word" => Variable::CrunchDelHighWord,
        "crunch_del_low_word" => Variable::CrunchDelLowWord,
        "crunch_small_outlines_size" => Variable::CrunchSmallOutlinesSize,
        "fixsp_small_outlines_size" => Variable::FixspSmallOutlinesSize,
        "superscript_worse_certainty" => Variable::SuperscriptWorseCertainty,
        "superscript_bettered_certainty" => Variable::SuperscriptBetteredCertainty,
        "superscript_scaledown_ratio" => Variable::SuperscriptScaledownRatio,
        "subscript_max_y_top" => Variable::SubscriptMaxYTop,
        "superscript_min_y_bottom" => Variable::SuperscriptMinYBottom,
        "suspect_rating_per_ch" => Variable::SuspectRatingPerCh,
        "suspect_accept_rating" => Variable::SuspectAcceptRating,
        "tessedit_lower_flip_hyphen" => Variable::TesseditLowerFlipHyphen,
        "tessedit_upper_flip_hyphen" => Variable::TesseditUpperFlipHyphen,
        "rej_whole_of_mostly_reject_word_fract" => Variable::RejWholeOfMostlyRejectWordFract,
        "min_orientation_margin" => Variable::MinOrientationMargin,
        "textord_tabfind_vertical_text_ratio" => Variable::TextordTabfindVerticalTextRatio,
        "textord_tabfind_aligned_gap_fraction" => Variable::TextordTabfindAlignedGapFraction,
        "tosp_old_sp_kn_th_factor" => Variable::TospOldSpKnThFactor,
        "tosp_threshold_bias1" => Variable::TospThresholdBias1,
        "tosp_threshold_bias2" => Variable::TospThresholdBias2,
        "tosp_narrow_fraction" => Variable::TospNarrowFraction,
        "tosp_narrow_aspect_ratio" => Variable::TospNarrowAspectRatio,
        "tosp_wide_fraction" => Variable::TospWideFraction,
        "tosp_wide_aspect_ratio" => Variable::TospWideAspectRatio,
        "tosp_fuzzy_space_factor" => Variable::TospFuzzySpaceFactor,
        "tosp_fuzzy_space_factor1" => Variable::TospFuzzySpaceFactor1,
        "tosp_fuzzy_space_factor2" => Variable::TospFuzzySpaceFactor2,
        "tosp_gap_factor" => Variable::TospGapFactor,
        "tosp_kern_gap_factor1" => Variable::TospKernGapFactor1,
        "tosp_kern_gap_factor2" => Variable::TospKernGapFactor2,
        "tosp_kern_gap_factor3" => Variable::TospKernGapFactor3,
        "tosp_ignore_big_gaps" => Variable::TospIgnoreBigGaps,
        "tosp_ignore_very_big_gaps" => Variable::TospIgnoreVeryBigGaps,
        "tosp_rep_space" => Variable::TospRepSpace,
        "tosp_enough_small_gaps" => Variable::TospEnoughSmallGaps,
        "tosp_table_kn_sp_ratio" => Variable::TospTableKnSpRatio,
        "tosp_table_xht_sp_ratio" => Variable::TospTableXhtSpRatio,
        "tosp_table_fuzzy_kn_sp_ratio" => Variable::TospTableFuzzyKnSpRatio,
        "tosp_fuzzy_kn_fraction" => Variable::TospFuzzyKnFraction,
        "tosp_fuzzy_sp_fraction" => Variable::TospFuzzySpFraction,
        "tosp_min_sane_kn_sp" => Variable::TospMinSaneKnSp,
        "tosp_init_guess_kn_mult" => Variable::TospInitGuessKnMult,
        "tosp_init_guess_xht_mult" => Variable::TospInitGuessXhtMult,
        "tosp_max_sane_kn_thresh" => Variable::TospMaxSaneKnThresh,
        "tosp_flip_caution" => Variable::TospFlipCaution,
        "tosp_large_kerning" => Variable::TospLargeKerning,
        "tosp_dont_fool_with_small_kerns" => Variable::TospDontFoolWithSmallKerns,
        "tosp_near_lh_edge" => Variable::TospNearLhEdge,
        "tosp_silly_kn_sp_gap" => Variable::TospSillyKnSpGap,
        "tosp_pass_wide_fuzz_sp_to_context" => Variable::TospPassWideFuzzSpToContext,
        "textord_noise_area_ratio" => Variable::TextordNoiseAreaRatio,
        "textord_initialx_ile" => Variable::TextordInitialxIle,
        "textord_initialasc_ile" => Variable::TextordInitialascIle,
        "textord_noise_sizelimit" => Variable::TextordNoiseSizelimit,
        "textord_noise_normratio" => Variable::TextordNoiseNormratio,
        "textord_noise_syfract" => Variable::TextordNoiseSyfract,
        "textord_noise_sxfract" => Variable::TextordNoiseSxfract,
        "textord_noise_hfract" => Variable::TextordNoiseHfract,
        "textord_noise_rowratio" => Variable::TextordNoiseRowratio,
        "textord_blshift_maxshift" => Variable::TextordBlshiftMaxshift,
        "textord_blshift_xfraction" => Variable::TextordBlshiftXfraction,
        _ => {
            return Err(Error::TesseractVariableName {
                value: s.as_ref().to_owned(),
            })
        }
    })
}
