Преобразование книги из *.txt в *.fb2

Здесь обсуждается всё, что касается макросов.
Post Reply
User avatar
buniak_a_h
Posts: 4262
Joined: Sat 20 Apr, 2013 00:17
Location: Санкт-Петробад
Has thanked: 222 times
Been thanked: 644 times
Contact:

Преобразование книги из *.txt в *.fb2

Post by buniak_a_h »

Сделал простенький макрос для преобразования электронной книги, записанной в текстовом виде, в формат fb2.
  1. -- txt2fb2.lua, Версия 3
  2.  
  3. local dtLast, bQuit=0, false
  4.  
  5. --local CR_LF='\r\n'
  6.  
  7. local CR_LF='\n'
  8.  
  9. local tin=table.insert
  10.  
  11. local F=far.Flags
  12.  
  13.  
  14.  
  15. local blank={
  16.  
  17. [[<?xml version="1.0" encoding="utf-8"?>]];
  18.  
  19. [[<FictionBook>]];
  20.  
  21. [[<description>]];
  22.  
  23. [[<title-info>]];
  24.  
  25. [[<genre>#Genre#</genre>]];
  26.  
  27. [[<author>]];
  28.  
  29. [[<first-name>#AuName#</first-name>]];
  30.  
  31. [[<last-name>#AuFam#</last-name>]];
  32.  
  33. [[</author>]];
  34.  
  35. [[<book-title>#Title#</book-title>]];
  36.  
  37. [[<annotation>#Annotation#</annotation>]];
  38.  
  39. [[<date>#Year#</date>]];
  40.  
  41. [[<lang>ru</lang>]];
  42.  
  43. [[<sequence name="#Seq_Name#" number="#Seq_Number#"/>]];
  44.  
  45. [[</title-info>]];
  46.  
  47. [[</description>]];
  48.  
  49. [[<body>]];
  50.  
  51. [[<title>]];
  52.  
  53. [[</title>]];
  54.  
  55. [[<section>]];
  56.  
  57. [[</section>]];
  58.  
  59. [[</body>]];
  60.  
  61. [[</FictionBook>]];
  62.  
  63. }
  64.  
  65.  
  66.  
  67. local GenreList = {
  68.  
  69.                    {Text='Фантастика, Фэнтези';
  70.  
  71.                     Sub={
  72.  
  73.                      {Code='sf_history'; Text='Альтернативная история'};
  74.  
  75.                      {Code='sf_action'; Text='Боевая фантастика'};
  76.  
  77.                      {Code='sf_epic'; Text='Эпическая фантастика'};
  78.  
  79.                      {Code='sf_heroic'; Text='Героическая фантастика'};
  80.  
  81.                      {Code='sf_detective'; Text='Детективная фантастика'};
  82.  
  83.                      {Code='sf_cyberpunk'; Text='Киберпанк'};
  84.  
  85.                      {Code='sf_space'; Text='Космическая фантастика'};
  86.  
  87.                      {Code='sf_social'; Text='Социально-философская фантастика'};
  88.  
  89.                      {Code='sf_horror'; Text='Ужасы и Мистика'};
  90.  
  91.                      {Code='sf_humor'; Text='Юмористическая фантастика'};
  92.  
  93.                      {Code='sf_fantasy'; Text='Фэнтези'};
  94.  
  95.                      {Code='sf'; Text='Научная фантастика'};
  96.  
  97.                      {Code='child_sf'; Text='Детская фантастика'};
  98.  
  99.                      {Code='love_sf'; Text='Любовная фантастика'};
  100.  
  101.                     };
  102.  
  103.                    };
  104.  
  105.                    {Text='Детективы и Боевики';
  106.  
  107.                     Sub={
  108.  
  109.                      {Code='det_classic'; Text='Классические детективы'};
  110.  
  111.                      {Code='det_police'; Text='Полицейские детективы'};
  112.  
  113.                      {Code='det_action'; Text='Боевики'};
  114.  
  115.                      {Code='det_irony'; Text='Иронические детективы'};
  116.  
  117.                      {Code='det_history'; Text='Исторические детективы'};
  118.  
  119.                      {Code='det_espionage'; Text='Шпионские детективы'};
  120.  
  121.                      {Code='det_crime'; Text='Криминальные детективы'};
  122.  
  123.                      {Code='det_political'; Text='Политические детективы'};
  124.  
  125.                      {Code='det_maniac'; Text='Маньяки'};
  126.  
  127.                      {Code='det_hard'; Text='Крутой детектив'};
  128.  
  129.                      {Code='thriller'; Text='Триллеры'};
  130.  
  131.                      {Code='detective'; Text='Детективы'};
  132.  
  133.                      {Code='sf_detective'; Text='НФ детективы'};
  134.  
  135.                      {Code='child_det'; Text='Детские детективы'};
  136.  
  137.                      {Code='love_detective'; Text='Остросюжетные любовные романы'};
  138.  
  139.                     };
  140.  
  141.                    };
  142.  
  143.                    {Text='Проза';
  144.  
  145.                     Sub={
  146.  
  147.                      {Code='prose_classic'; Text='Классическая проза'};
  148.  
  149.                      {Code='prose_history'; Text='Историческая проза'};
  150.  
  151.                      {Code='prose_contemporary'; Text='Современная проза'};
  152.  
  153.                      {Code='prose_counter'; Text='Контркультура'};
  154.  
  155.                      {Code='prose_rus_classic'; Text='Русская классика'};
  156.  
  157.                      {Code='prose_su_classics'; Text='Советская классика'};
  158.  
  159.                      {Code='humor_prose'; Text='Юмористическая проза'};
  160.  
  161.                      {Code='prose_military'; Text='Военная проза'};
  162.  
  163.                      {Code='child_prose'; Text='Детская проза'}
  164.  
  165.                     }
  166.  
  167.                    };
  168.  
  169.                    {Text='Любовные романы';
  170.  
  171.                     Sub={
  172.  
  173.                      {Code='love_contemporary'; Text='Современные любовные романы'};
  174.  
  175.                      {Code='love_history'; Text='Исторические любовные романы'};
  176.  
  177.                      {Code='love_detective'; Text='Остросюжетные любовные романы'};
  178.  
  179.                      {Code='love_short'; Text='Короткие любовные романы'};
  180.  
  181.                      {Code='love_erotica'; Text='Эротика'};
  182.  
  183.                      {Code='love_sf'; Text='Любовная фантастика'}
  184.  
  185.                     }
  186.  
  187.                    };
  188.  
  189.                    {Text='Приключения';
  190.  
  191.                     Sub={
  192.  
  193.                      {Code='adv_western'; Text='Вестерны'};
  194.  
  195.                      {Code='adv_history'; Text='Исторические приключения'};
  196.  
  197.                      {Code='adv_indian'; Text=' Приключения: Индейцы'};
  198.  
  199.                      {Code='adv_maritime'; Text=' Морские приключения'};
  200.  
  201.                      {Code='adv_geo'; Text=' Путешествия и География'};
  202.  
  203.                      {Code='adv_animal'; Text=' Природа и Животные'};
  204.  
  205.                      {Code='adventure'; Text=' Приключения: Прочее'};
  206.  
  207.                      {Code='child_adv'; Text=' Детские приключения'};
  208.  
  209.                     }
  210.  
  211.                    };
  212.  
  213.                    {Text='Детские';
  214.  
  215.                     Sub={
  216.  
  217.                      {Code='child_tale'; Text='Сказки'};
  218.  
  219.                      {Code='child_verse'; Text='Детские стихи'};
  220.  
  221.                      {Code='child_prose'; Text='Детская проза'};
  222.  
  223.                      {Code='child_sf'; Text='Детская фантастика'};
  224.  
  225.                      {Code='child_det'; Text='Детские остросюжетные'};
  226.  
  227.                      {Code='child_adv'; Text='Детские приключения'};
  228.  
  229.                      {Code='child_education'; Text='Детская образовательная'};
  230.  
  231.                      {Code='children'; Text='Детские: Прочее'}
  232.  
  233.                     }
  234.  
  235.                    };
  236.  
  237.                    {Text='Поэзия и драматургия';
  238.  
  239.                     Sub={
  240.  
  241.                      {Code='dramaturgy'; Text='Драматургия'};
  242.  
  243.                      {Code='poetry'; Text='Поэзия'};
  244.  
  245.                      {Code='humor_verse'; Text='Юмористические стихи'};
  246.  
  247.                      {Code='child_verse'; Text='Детские стихи'}
  248.  
  249.                     }
  250.  
  251.                    };
  252.  
  253.                    {Text='Старинная литература';
  254.  
  255.                     Sub={
  256.  
  257.                      {Code='antique_ant'; Text='Античная литература'};
  258.  
  259.                      {Code='antique_european'; Text='Европейская старинная литература'};
  260.  
  261.                      {Code='antique_russian'; Text='Древнерусская литература'};
  262.  
  263.                      {Code='antique_east'; Text='Древневосточная литература'};
  264.  
  265.                      {Code='antique_myths'; Text='Мифы. Легенды. Эпос'};
  266.  
  267.                      {Code='antique'; Text='Старинная литература: Прочее'}
  268.  
  269.                     }
  270.  
  271.                    };
  272.  
  273.                    {Text='Научно-образовательная';
  274.  
  275.                     Sub={
  276.  
  277.                      {Code='sci_history'; Text='История'};
  278.  
  279.                      {Code='sci_psychology'; Text='Психология'};
  280.  
  281.                      {Code='sci_culture'; Text='Культурология'};
  282.  
  283.                      {Code='sci_religion'; Text='Религиоведение'};
  284.  
  285.                      {Code='sci_philosophy'; Text='Философия'};
  286.  
  287.                      {Code='sci_politics'; Text='Политика'};
  288.  
  289.                      {Code='sci_juris'; Text='Юриспруденция'};
  290.  
  291.                      {Code='sci_linguistic'; Text='Языкознание'};
  292.  
  293.                      {Code='sci_medicine'; Text='Медицина'};
  294.  
  295.                      {Code='sci_phys'; Text='Физика'};
  296.  
  297.                      {Code='sci_math'; Text='Математика'};
  298.  
  299.                      {Code='sci_chem'; Text='Химия'};
  300.  
  301.                      {Code='sci_biology'; Text='Биология'};
  302.  
  303.                      {Code='sci_tech'; Text='Технические'};
  304.  
  305.                      {Code='science'; Text='Научно-образовательная: Прочее'};
  306.  
  307.                      {Code='adv_animal'; Text='Природа и Животные'};
  308.  
  309.                     }
  310.  
  311.                    };
  312.  
  313.                    {Text='Компьютеры и Интернет';
  314.  
  315.                     Sub={
  316.  
  317.                      {Code='comp_www'; Text='Интернет'};
  318.  
  319.                      {Code='comp_programming'; Text='Программирование'};
  320.  
  321.                      {Code='comp_hard'; Text='Компьютерное Железо'};
  322.  
  323.                      {Code='comp_soft'; Text='Программы'};
  324.  
  325.                      {Code='comp_db'; Text='Базы данных'};
  326.  
  327.                      {Code='comp_osnet'; Text='ОС и Сети'};
  328.  
  329.                      {Code='computers'; Text='Компьютеры и Интернет: Прочее'}
  330.  
  331.                     }
  332.  
  333.                    };
  334.  
  335.                    {Text='Справочная литература';
  336.  
  337.                     Sub={
  338.  
  339.                      {Code='ref_encyc'; Text='Энциклопедии'};
  340.  
  341.                      {Code='ref_dict'; Text='Словари'};
  342.  
  343.                      {Code='ref_ref'; Text='Справочники'};
  344.  
  345.                      {Code='ref_guide'; Text='Путеводители'};
  346.  
  347.                      {Code='reference'; Text='Справочная литература: Прочее'}
  348.  
  349.                     }
  350.  
  351.                    };
  352.  
  353.                    {Text='Документальное';
  354.  
  355.                     Sub={
  356.  
  357.                      {Code='nonf_biography'; Text='Биографии и Мемуары'};
  358.  
  359.                      {Code='nonf_publicism'; Text='Публицистика'};
  360.  
  361.                      {Code='nonf_criticism'; Text='Критика'};
  362.  
  363.                      {Code='nonfiction'; Text='Документальное: Прочее'};
  364.  
  365.                      {Code='design'; Text='Искусство'};
  366.  
  367.                      {Code='adv_animal'; Text='Природа и Животные'}
  368.  
  369.                     }
  370.  
  371.                    };
  372.  
  373.                    {Text='Религия и Духовность';
  374.  
  375.                     Sub={
  376.  
  377.                      {Code='religion_rel'; Text='Религия'};
  378.  
  379.                      {Code='religion_esoterics'; Text='Эзотерика'};
  380.  
  381.                      {Code='religion_self'; Text='Самосовершенствование'};
  382.  
  383.                      {Code='religion'; Text='Религия и духовность: Прочее'};
  384.  
  385.                      {Code='sci_religion'; Text='Религиозное образование'}
  386.  
  387.                     }
  388.  
  389.                    };
  390.  
  391.                    {Text='Юмор';
  392.  
  393.                     Sub={
  394.  
  395.                      {Code='humor_anecdote'; Text='Анекдоты'};
  396.  
  397.                      {Code='humor_prose'; Text='Юмористическая проза'};
  398.  
  399.                      {Code='humor_verse'; Text='Юмористические стихи'};
  400.  
  401.                      {Code='humor'; Text='Юмор: Прочее'}
  402.  
  403.                     }
  404.  
  405.                    };
  406.  
  407.                    {Text='Дом и Семья';
  408.  
  409.                     Sub={
  410.  
  411.                      {Code='home_cooking'; Text='Кулинария'};
  412.  
  413.                      {Code='home_pets'; Text='Домашние животные'};
  414.  
  415.                      {Code='home_crafts'; Text='Хобби и Ремесла'};
  416.  
  417.                      {Code='home_entertain'; Text='Развлечения'};
  418.  
  419.                      {Code='home_health'; Text='Здоровье'};
  420.  
  421.                      {Code='home_garden'; Text='Сад и Огород'};
  422.  
  423.                      {Code='home_diy'; Text='Самоделки'};
  424.  
  425.                      {Code='home_sport'; Text='Спорт'};
  426.  
  427.                      {Code='home_sex'; Text='Эротика и Секс'};
  428.  
  429.                      {Code='home'; Text='Дом и Семья: Прочее'};
  430.  
  431.                      {Code='geo_guides'; Text='ГеоПутеводитель'}
  432.  
  433.                     }
  434.  
  435.                    };
  436.  
  437.                    {Text='Экономика, Бизнес';
  438.  
  439.                     Sub={
  440.  
  441.                      {Code='job_hunting'; Text='Поиск работы, карьера'};
  442.  
  443.                      {Code='management'; Text='Управление, подбор персонала'};
  444.  
  445.                      {Code='marketing'; Text='Маркетинг, PR, реклама'};
  446.  
  447.                      {Code='banking'; Text='Банковское дело'};
  448.  
  449.                      {Code='stock'; Text='Ценные бумаги, инвестиции'};
  450.  
  451.                      {Code='accounting'; Text='Бухучет, налогообложение, аудит'};
  452.  
  453.                      {Code='global_economy'; Text='Внешнеэкономическая деятельность'};
  454.  
  455.                      {Code='economics'; Text='Экономика'};
  456.  
  457.                      {Code='industries'; Text='Отраслевые издания'};
  458.  
  459.                      {Code='org_behavior'; Text='Корпоративная культура'};
  460.  
  461.                      {Code='personal_finance'; Text='Личные финансы'};
  462.  
  463.                      {Code='real_estate'; Text='Real Недвижимость'};
  464.  
  465.                      {Code='popular_business'; Text='О бизнесе популярно'};
  466.  
  467.                      {Code='small_business'; Text='Малый бизнес'};
  468.  
  469.                      {Code='paper_work'; Text='Paper Делопроизводство'};
  470.  
  471.                      {Code='economics_ref Справочники'}
  472.  
  473.                     }
  474.  
  475.                    };
  476.  
  477.                   }
  478.  
  479. local function OEMtoUTF_8(str)
  480.  
  481.  return win.WideCharToMultiByte(win.MultiByteToWideChar(str, win.GetOEMCP()), 65001)
  482.  
  483. end
  484.  
  485.  
  486.  
  487. local function AddLine(Line, hOut)
  488.  
  489.  --editor.InsertString()
  490.  
  491.  --editor.SetString(nil, Editor.Lines-1, Line, CR_LF)
  492.  
  493.  hOut:write(( Line:match('^(.-)%s*$') or Line)..CR_LF)
  494.  
  495. end
  496.  
  497.  
  498.  
  499. local ampsims={
  500.  
  501.       ['\'']='&apos;';
  502.  
  503.       ['"']='&quot;';
  504.  
  505.       ['&']='&amp;';
  506.  
  507.       ['<']='&lt;';
  508.  
  509.       ['>']='&gt;';
  510.  
  511.       ['©']='&copy;';
  512.  
  513.       ['®']='&reg;';
  514.  
  515.       }
  516.  
  517. local function ToAmp(Str)
  518.  
  519.  -- Замена символов на &-последовательности
  520.  
  521.  local r=''
  522.  
  523.  local iPos
  524.  
  525.  repeat
  526.  
  527.   --iPos = unicode.utf8.cfind(Str, '[&\'"$<>©®]')
  528.  
  529.   iPos = Str:cfind('[&\'"$<>©®]')
  530.  
  531.   if iPos then
  532.  
  533.    r = r .. Str:sub(1,iPos-1) .. ampsims[Str:sub(iPos,iPos)]
  534.  
  535.    Str = Str:sub(iPos+1)
  536.  
  537.   end
  538.  
  539.  until not iPos
  540.  
  541.  if Str ~= '' then
  542.  
  543.   r = r .. Str
  544.  
  545.  end
  546.  
  547.  return r
  548.  
  549. end
  550.  
  551.  
  552.  
  553.  
  554.  
  555. local function _txt2fb2(fn, CanQuit)
  556.  
  557.  Far.DisableHistory(1)
  558.  
  559.  local DlgItems, ID, y = {}, {}
  560.  
  561.  local DlgGUID = win.Uuid('8071F9E9-8E86-415E-B2F8-6901B187E374')
  562.  
  563.  local DlgResult
  564.  
  565.  tin(DlgItems, { "DI_DOUBLEBOX", 0, 0, 73, 15, 0, 0, 0, 0, 'Преобразование '..fn..' в FB2' })
  566.  
  567.  y = 1
  568.  
  569.  tin(DlgItems, {'DI_TEXT',3,y,0,y,0,0,0,0,"Жанр"})
  570.  
  571.  y= y + 1
  572.  
  573.  tin(DlgItems, {'DI_COMBOBOX', 3,y,27,y, GenreList,0,0,{DIF_DROPDOWNLIST=1;DIF_CENTERGROUP=1}})
  574.  
  575.  ID.Genre=#DlgItems
  576.  
  577.  tin(DlgItems, {'DI_COMBOBOX', 30,y,68,y, GenreList[1].Sub,0,0,{DIF_DROPDOWNLIST=1;DIF_CENTERGROUP=1}})
  578.  
  579.  ID.SubGenre=#DlgItems
  580.  
  581.  y = y + 2
  582.  
  583.  tin(DlgItems, {"DI_TEXT", 3,y,0,y,0,0,0,0,"Данные автора:"})
  584.  
  585.  y = y + 1
  586.  
  587.  tin(DlgItems, {"DI_TEXT", 3,y,0,y,0,0,0,0,"имя"})
  588.  
  589.  tin(DlgItems, {"DI_EDIT", 7,y,33,y,0,'Txt2FB.AuName',0,{DIF_SELECTONENTRY=1; DIF_HISTORY=1;DIF_USELASTHISTORY=1}})
  590.  
  591.  ID.AuName=#DlgItems
  592.  
  593.  tin(DlgItems, {"DI_TEXT", 35,y,0,y,0,0,0,0,"фамилия"})
  594.  
  595.  tin(DlgItems, {"DI_EDIT",  43,y,68,y,0,'Txt2FB.AuFam',0,{DIF_SELECTONENTRY=1; DIF_HISTORY=1;DIF_USELASTHISTORY=1}})
  596.  
  597.  ID.AuFam=#DlgItems
  598.  
  599.  y = y + 2
  600.  
  601.  tin(DlgItems, {"DI_TEXT", 3,y,0,y,0,0,0,0,"Серия"})
  602.  
  603.  tin(DlgItems, {"DI_EDIT", 9,y,60,y,0,'Txt2FB.Seq_Name',0,{DIF_SELECTONENTRY=1;DIF_HISTORY=1;DIF_USELASTHISTORY=1}})
  604.  
  605.  ID.Seq_Name=#DlgItems
  606.  
  607.  tin(DlgItems, {"DI_TEXT", 63,y,0,y,0,0,0,0,"№"})
  608.  
  609.  tin(DlgItems, {"DI_EDIT", 65,y,68,y,0,0,0,{DIF_SELECTONENTRY=1}})
  610.  
  611.  ID.Seq_Number=#DlgItems
  612.  
  613.  y = y + 1
  614.  
  615.  tin(DlgItems, {"DI_TEXT", -1,y,0,y,0,0,0,{DIF_SEPARATOR=1}})
  616.  
  617.  y = y + 1
  618.  
  619.  tin(DlgItems, {"DI_TEXT", 3,y,0,y,0,0,0,0, "Название книги"})
  620.  
  621.  y = y + 1
  622.  
  623.  tin(DlgItems, {"DI_EDIT", 3,y,65,y,0,0,0,{DIF_SELECTONENTRY=1}})
  624.  
  625.  ID.Title=#DlgItems
  626.  
  627.  y = y + 1
  628.  
  629.  tin(DlgItems, {"DI_TEXT", 3,y,0,y,0,0,0,0, "Аннотация"})
  630.  
  631.  y = y + 1
  632.  
  633.  tin(DlgItems, {"DI_FIXEDIT", 3,y,68,y,0,0,0,{DIF_EDITOR=1}})
  634.  
  635.  ID.Ann_1=#DlgItems
  636.  
  637.  y = y + 1
  638.  
  639.  tin(DlgItems, {"DI_FIXEDIT", 3,y,68,y,0,0,0,{DIF_EDITOR=1}})
  640.  
  641.  y = y + 1
  642.  
  643.  tin(DlgItems, {"DI_FIXEDIT", 3,y,68,y,0,0,0,{DIF_EDITOR=1}})
  644.  
  645.  y = y + 1
  646.  
  647.  tin(DlgItems, {"DI_EDIT", 3,y,68,y,0,0,0,{DIF_EDITOR=1}})
  648.  
  649.  ID.Ann_2=#DlgItems
  650.  
  651.  y = y + 2
  652.  
  653.  tin(DlgItems, {"DI_BUTTON", 0,y,0,y,0,0,0,{DIF_CENTERGROUP=1; DIF_DEFAULTBUTTON=1},"Ок"})
  654.  
  655.  ID.OkBtn=#DlgItems
  656.  
  657.  tin(DlgItems, {"DI_BUTTON", 0,y,0,y,0,0,0,{DIF_CENTERGROUP=1},"Cancel"})
  658.  
  659.  ID.CancelBtn=#DlgItems
  660.  
  661.  if CanQuit then
  662.  
  663.   tin(DlgItems, {"DI_BUTTON", 0,y,0,y,0,0,0,{DIF_CENTERGROUP=1},"&Quit"})
  664.  
  665.   ID.QuitBtn=#DlgItems
  666.  
  667.  end
  668.  
  669.  
  670.  
  671.  local function _DlgProc(hDlg, Msg, ItemId, Param2)
  672.  
  673.   if Msg == F.DN_INITDIALOG then
  674.  
  675.    hDlg:send("DM_LISTSETCURPOS", ID.Genre, {SelectPos=1; TopPos=1})
  676.  
  677.    hDlg:send("DM_LISTSETCURPOS", ID.SubGenre, {SelectPos=1})
  678.  
  679.   elseif Msg == F.DN_DROPDOWNOPENED and
  680.  
  681.          ItemId == ID.Genre and
  682.  
  683.          Param2 == 0 then
  684.  
  685.    hDlg:send( "DM_LISTSET", ID.SubGenre, GenreList[hDlg:send("DM_LISTGETCURPOS", ID.Genre).SelectPos].Sub )
  686.  
  687.    hDlg:send("DM_LISTSETCURPOS", ID.SubGenre, {SelectPos=1})
  688.  
  689.   elseif Msg == F.DN_CLOSE then
  690.  
  691.    if ItemId < 0 or ItemId == ID.CancelBtn or ItemId == ID.QuitBtn then
  692.  
  693.     DlgResult = nil
  694.  
  695.     bQuit = ItemId == ID.QuitBtn
  696.  
  697.    else
  698.  
  699.     DlgResult = {}
  700.  
  701.     local i1, i2 = hDlg:send("DM_LISTGETCURPOS", ID.Genre).SelectPos,
  702.  
  703.                    hDlg:send("DM_LISTGETCURPOS", ID.SubGenre).SelectPos
  704.  
  705.     DlgResult.Genre = GenreList[i1].Sub[i2].Code
  706.  
  707.     DlgResult.AuName = hDlg:send("DM_GETTEXT", ID.AuName)
  708.  
  709.     DlgResult.AuFam = hDlg:send("DM_GETTEXT", ID.AuFam)
  710.  
  711.     DlgResult.Title = hDlg:send("DM_GETTEXT", ID.Title)
  712.  
  713.     DlgResult.Seq_Name =  hDlg:send("DM_GETTEXT", ID.Seq_Name)
  714.  
  715.     DlgResult.Seq_Number =  hDlg:send("DM_GETTEXT", ID.Seq_Number)
  716.  
  717.     local s=''
  718.  
  719.     for i=ID.Ann_1,ID.Ann_2 do
  720.  
  721.      s = s .. mf.trim(hDlg:send("DM_GETTEXT", i)) .. ' '
  722.  
  723.     end
  724.  
  725.     DlgResult.Annotation =  mf.trim(s)
  726.  
  727.     if mf.trim(DlgResult.AuName or '') == '' or
  728.  
  729.        mf.trim(DlgResult.AuFam or '') == '' then
  730.  
  731.      far.Message('Необходимо указать имя и фамилию автора!','Не заполнены обязательные поля',';Ok','w')
  732.  
  733.      hDlg:send("DM_SETFOCUS", mf.trim(DlgResult.AuName or '') == '' and ID.AuName or ID.AuFam)
  734.  
  735.      return false
  736.  
  737.     end
  738.  
  739.    end
  740.  
  741.   end
  742.  
  743.  end -- _DlgProc
  744.  
  745.  DlgItems[1][5] = y+1
  746.  
  747.  far.Dialog(DlgGUID,-1,5,73,6+y, nil, DlgItems, "FDLG_SMALLDIALOG", _DlgProc)
  748.  
  749.  if not DlgResult then return end
  750.  
  751.  
  752.  
  753.  local fnOut= (Area.Shell and APanel.Path .. '\\' or '') .. mf.fsplit(fn, 7)..'.fb2'
  754.  
  755.  if mf.fexist(fnOut) then
  756.  
  757.   if far.Message('Файл '..fnOut..' существует.\nПереписать?',
  758.  
  759.                  'Создание файла fb2', ';YesNo', 'w') == 1 then
  760.  
  761.     win.DeleteFile(fnOut)
  762.  
  763.   else
  764.  
  765.    return false
  766.  
  767.   end
  768.  
  769.  end
  770.  
  771.  local hOut, msg=io.open(fnOut, 'w')
  772.  
  773.  if not hOut then
  774.  
  775.   far.Message('Неудача создания файла\n'..fnOut..'\n'..msg,'txt -> fb2', nil, 'w' )
  776.  
  777.   return false
  778.  
  779.  end
  780.  
  781.  for _,Line in ipairs(blank) do
  782.  
  783.   for w in Line:gmatch('#([%w_]+)#') do
  784.  
  785.    Line = Line:gsub('#'..w..'#', ToAmp(DlgResult[w] or '') )
  786.  
  787.   end
  788.  
  789.   AddLine(Line, hOut)
  790.  
  791.   if mf.trim(Line) == '<section>' then
  792.  
  793.    local iEmpL, sOut=0, ''
  794.  
  795.    local function AddLine1(Line1, ii)
  796.  
  797.     if Line1:match('%S') then -- прочитана непустая строка
  798.  
  799.      if Line1:match('^%s+') then -- строка начинается с пробелов (начало абзаца)
  800.  
  801.       if iEmpL > 1 then -- был разделитель - выталкиваю
  802.  
  803.        AddLine('<p><subtitle>* * *</subtitle></p>', hOut)
  804.  
  805.       end
  806.  
  807.       iEmpL = 0
  808.  
  809.       if sOut:match('%S') then -- Уже была сформирована непустая строка - выталкиваю
  810.  
  811.        AddLine('<p>'..ToAmp(sOut:match('^%s*(.*%S)%s*$'):gsub('%s+',' '))..'</p>', hOut)
  812.  
  813.        sOut = ''
  814.  
  815.       end
  816.  
  817.      elseif iEmpL > 0 then
  818.  
  819.       -- Строка начинается не с пробелов, но ей предшествует пустая - тоже абзац
  820.  
  821.       if sOut:match('%S') then
  822.  
  823.        AddLine('<p>     '..ToAmp(sOut:match('^%s*(.*%S)%s*$'):gsub('%s+',' '))..'</p>', hOut)
  824.  
  825.        sOut = ''
  826.  
  827.       end
  828.  
  829.       iEmpL = 0
  830.  
  831.      end -- Line1 - непустая
  832.  
  833.      local function _ItIsHdr(L)
  834.  
  835.       local l1,l2, l3=L:match('^(%s+)(.-)(%s+)$')
  836.  
  837.       return l1 and l2 and l3 and l2:match('^[%d%u][%d%u.%s -/]+$') and (math.abs(l1:len()-l3:len()) <= 2)
  838.  
  839.      end
  840.  
  841.      if _ItIsHdr(Line1) then
  842.  
  843.       if sOut:match('%S') then
  844.  
  845.        AddLine(sOut, hOut)
  846.  
  847.        sOut = ''
  848.  
  849.       end
  850.  
  851.       AddLine('</section><section><title><p>'..Line1..'</p></title>', hOut)
  852.  
  853.      else
  854.  
  855.       sOut = sOut .. Line1 .. ' '
  856.  
  857.      end
  858.  
  859.     else -- пустая строка; подсчитываю колиичество
  860.  
  861.      iEmpL = iEmpL + 1
  862.  
  863.     end
  864.  
  865.    end -- function AddLine1
  866.  
  867.    far.Message('Создание\n'..mf.fsplit(fnOut, 12),'','')
  868.  
  869.    local aLines={}
  870.  
  871.    if Area.Editor then
  872.  
  873.     for i=1,Editor.Lines do
  874.  
  875.      aLines[i] = Editor.GetStr(i)
  876.  
  877.     end
  878.  
  879.    else
  880.  
  881.     for ll in io.lines(fn) do
  882.  
  883.      aLines[#aLines+1] = OEMtoUTF_8(ll):gsub('[\1-\31]+', ' ')
  884.  
  885.     end
  886.  
  887.    end
  888.  
  889.    -- Вычисление правой границы
  890.  
  891.    local rb=0
  892.  
  893.    for _, Line1 in ipairs(aLines) do
  894.  
  895.     rb = math.max(rb,Line1:len())
  896.  
  897.    end
  898.  
  899.    for ii, Line1 in ipairs(aLines) do
  900.  
  901.     AddLine1(mf.strpad(Line1, rb), ii )
  902.  
  903.    end -- for Line1 in io.lines(...)
  904.  
  905.    if sOut:match('%S') then
  906.  
  907.     AddLine('<p>'..sOut:match('^%s*(.*%S)%s*$'):gsub('%s+',' ')..'</p>', hOut)
  908.  
  909.    end
  910.  
  911.    far.AdvControl("ACTL_REDRAWALL")
  912.  
  913.   end
  914.  
  915.  end
  916.  
  917.  hOut:close()
  918.  
  919. end -- _txt2fb2
  920.  
  921.  
  922.  
  923. local function txt2fb2()
  924.  
  925.  local PanItem
  926.  
  927.  local aFn={}
  928.  
  929.  bQuit = false
  930.  
  931.  if APanel.Selected then
  932.  
  933.   for i=1,APanel.SelCount do
  934.  
  935.    PanItem = panel.GetSelectedPanelItem(nil, 1, i)
  936.  
  937.    if not PanItem.FileAttributes:match('d') and
  938.  
  939.       far.ProcessName("PN_CMPNAME", '*.txt', PanItem.FileName) then
  940.  
  941.     tin(aFn, PanItem.FileName)
  942.  
  943.    end
  944.  
  945.   end
  946.  
  947.   for i,v in ipairs(aFn) do
  948.  
  949.     _txt2fb2(v, i<#aFn)
  950.  
  951.     if bQuit then break end
  952.  
  953.   end
  954.  
  955.  else
  956.  
  957.   _txt2fb2(APanel.Current, false)
  958.  
  959.  end
  960.  
  961. end
  962.  
  963.  
  964.  
  965. --------------------------------------------------
  966.  
  967. Macro{
  968.  
  969. --  id="";
  970.  
  971.   area="Shell";
  972.  
  973.   key="CtrlShiftB-F";
  974.  
  975.   description="F - Преобразование txt в fb2";
  976.  
  977. --  filemask="";
  978.  
  979.   flags="";
  980.  
  981. --  priority=50;
  982.  
  983. --  sortpriority=50;
  984.  
  985.   condition=function(_)
  986.  
  987.    local bRes=false
  988.  
  989.    if APanel.Selected then
  990.  
  991.     local PanItem
  992.  
  993.     for i=1,APanel.SelCount do
  994.  
  995.      PanItem = panel.GetSelectedPanelItem(nil, 1, i)
  996.  
  997.      if not PanItem.FileAttributes:match('d') and
  998.  
  999.         far.ProcessName("PN_CMPNAME", '*.txt', PanItem.FileName) then
  1000.  
  1001.       bRes = true
  1002.  
  1003.       break
  1004.  
  1005.      end
  1006.  
  1007.     end
  1008.  
  1009.    else
  1010.  
  1011.     bRes = (not APanel.Folder and far.ProcessName("PN_CMPNAME", '*.txt', APanel.Current) )
  1012.  
  1013.    end
  1014.  
  1015.    if bRes then
  1016.  
  1017.     dtLast = 0
  1018.  
  1019.    else
  1020.  
  1021.     if dtLast == 0 then
  1022.  
  1023.      dtLast = os.time()
  1024.  
  1025.     elseif os.time()-dtLast < 10 then
  1026.  
  1027.      dtLast = 0 --os.time()
  1028.  
  1029.      far.Message('Необходимо либо установить указатель на файл *.txt,\nлибо отметить хотя бы один такой файл',
  1030.  
  1031.                  'Ошибка вызова процедуры', ';Ok', 'w')
  1032.  
  1033.     end
  1034.  
  1035.    end
  1036.  
  1037.    return bRes
  1038.  
  1039.   end;
  1040.  
  1041. action=function()
  1042.  
  1043.  txt2fb2()
  1044.  
  1045. end;
  1046.  
  1047. }
  1048.  
  1049.  
  1050.  
  1051. Macro{
  1052.  
  1053. --  id="";
  1054.  
  1055.   area="Editor";
  1056.  
  1057.   key="CtrlShiftB-F";
  1058.  
  1059.   description="F - Преобразование txt в fb2";
  1060.  
  1061.   filemask="*.txt";
  1062.  
  1063.   flags="";
  1064.  
  1065. --  priority=50;
  1066.  
  1067. --  sortpriority=50;
  1068.  
  1069. --  condition=function(key) return true end;
  1070.  
  1071. action=function()
  1072.  
  1073.  _txt2fb2(Editor.FileName, false)
  1074.  
  1075. end;
  1076.  
  1077. }

Т.к. несколько человек скачали (и парочка даже поблагодарили :) ), решил подправить некоторые недочёты.


"Защита от дурака" по некоторым моментам (например, файл fb2 существует; запись в папку запрещена и т.п.) не реализована. Если кто-нибудь заинтересуется - пишите, доделаю...
В макросе создание "повешено" на сочетание CtrlShiftB-F, что предполагает наличие макроса, позволяющего вводить "сложные" сочетания клавиш. Настройка не предусмотрена.
Работоспособность макроса проверена чтением результата программой Cool Reader 2
txt2fb2.lua.7z
txt2fb2.lua, Версия 3
(5.85 KiB) Downloaded 91 times
Last edited by buniak_a_h on Wed 26 Sep, 2018 03:28, edited 2 times in total.
User avatar
HaRT
Moderator
Posts: 10863
Joined: Tue 30 Aug, 2005 17:21
Has thanked: 221 times
Been thanked: 358 times

Преобразование книги из *.txt в *.fb2

Post by HaRT »

buniak_a_h wrote: Mon 12 Mar, 2018 10:39Разбивает форматированный текстовый файл на параграфы (началом параграфа считается строка, начинающаяся с пробелов
Судя по описанию, имеются в виду всё-таки абзацы. Слово «параграф» в русском языке имеет другой смысл.
Фар есть инструмент, а не нянька. © 2009 DrKnS
User avatar
buniak_a_h
Posts: 4262
Joined: Sat 20 Apr, 2013 00:17
Location: Санкт-Петробад
Has thanked: 222 times
Been thanked: 644 times
Contact:

Преобразование книги из *.txt в *.fb2

Post by buniak_a_h »

HaRT wrote: Mon 12 Mar, 2018 12:10
buniak_a_h wrote: Mon 12 Mar, 2018 10:39Разбивает форматированный текстовый файл на параграфы (началом параграфа считается строка, начинающаяся с пробелов
Судя по описанию, имеются в виду всё-таки абзацы. Слово «параграф» в русском языке имеет другой смысл.
Параграфы в терминологии MS Word. Т.е. текст, отделённые от впереди идущего жёстким переносом строки.
Впрочем, Вы, кажется, поняли...
Ну и цитата из описания html (тег <p>):
<p> Создает новый параграф
Впрочем, в другом документе
Тег <p> Определяет текстовый абзац
Видимо, терминология не устоялась. Хотя абзац - действительно логичнее
Last edited by buniak_a_h on Mon 12 Mar, 2018 12:38, edited 1 time in total.
User avatar
HaRT
Moderator
Posts: 10863
Joined: Tue 30 Aug, 2005 17:21
Has thanked: 221 times
Been thanked: 358 times

Преобразование книги из *.txt в *.fb2

Post by HaRT »

buniak_a_h wrote: Mon 12 Mar, 2018 12:30Видимо, терминология не устоялась. Хотя абзац - действительно логичнее
Терминология уже сто лет как устоялась. Английское paragraph в типографском значении переводится как абзац. Без вариантов.
Фар есть инструмент, а не нянька. © 2009 DrKnS
User avatar
buniak_a_h
Posts: 4262
Joined: Sat 20 Apr, 2013 00:17
Location: Санкт-Петробад
Has thanked: 222 times
Been thanked: 644 times
Contact:

Преобразование книги из *.txt в *.fb2

Post by buniak_a_h »

Вышла 3-я версия макроса. Он теперь умеет распознавать заголовки (отцентрированные, напечатанные в верхнем регистре, но - увы - однострочные). Таким образом они попадают в оглавление, распознаваемое программами Cool Reader, OReader и NeoReader (две последние поставляются с электронной читалкой Monte Cristo)
Post Reply

Return to “Обсуждение макросов”