# $Id$ $VERSION{''.__FILE__} = '$Revision$'; # # >>Title:: MIF Format Driver # # >>Copyright:: # Copyright (c) 1992-1996, Ian Clatworthy (ianc@mincom.com). # You may distribute under the terms specified in the LICENSE file. # # >>History:: # ----------------------------------------------------------------------- # Date Who Change # 01-Mar-98 ianc Inner and Outer alignment of tables now supported # David Schooley's patch to MifPathName also applied. # 29-Feb-96 ianc SDF 2.000 # ----------------------------------------------------------------------- # # >>Purpose:: # This library provides an [[SDF_DRIVER]] which generates # [[FrameMaker]]'s [[MIF]] (Maker Interchange Format) files. # # >>Description:: # Variables supported include: # # * {{MIF_EXT}} - the extension to use for Frame files in jumps # * {{MIF_BOOK_MODE}} - build a book, rather than a normal document # * {{MIF_ALL_STYLES}} - output all paragraph styles, rather than # just the used ones. # # >>Limitations:: # Changing a font family or style does not update the FPostScriptName # attribute. In practice, this may not be a problem as it may be # fixed by [[FrameMaker]] during the loading of a [[MIF]] file. # # Is _MifProcessControls() the best way to handle document control settings? # (There may be problems with only processing DOC_* variables at the end.) # # We do not support a Running header/footer type of {{paratag}} as # inserting the name of a Frame paragraph tag would probably be # meaningless. # # Only the predefined colours are currently supported. Ultimately, # the RGB value of an arbitary color could be converted to CMYK and # added to the color catalog. # # >>Resources:: # # >>Implementation:: # ##### Constants ##### # FrameMaker 5.0 adds blank pages for tables with titles, so # this configuration flag controls whether # we produce real or simple/simulated table titles. # Unless a book is being generated, generating a list of tables # only works when table titles are separate paragraphs. $_MIF_SIMPLE_TBL_TITLES = 1; # Define the set of paragraph attributes. @MIF_PARA_ATTRS = ( 'AcrobatLevel', 'Alignment', 'AutoNum', 'BlockSize', 'BotSepAtIndent', 'BotSepOffset', 'BotSeparator', 'CellAlignment', 'CellBMarginFixed', 'CellLMarginFixed', 'CellMargins', 'CellRMarginFixed', 'CellTMarginFixed', 'FIndent', 'FIndentOffset', 'FIndentRelative', 'FontAngle', 'FontCase', 'FontChangeBar', 'FontColor', 'FontDW', 'FontDX', 'FontDY', 'FontFamily', 'FontLocked', 'FontOutline', 'FontOverline', 'FontPairKern', 'FontPosition', 'FontSeparation', 'FontShadow', 'FontSize', 'FontStrike', 'FontUnderlining', 'FontVar', 'FontWeight', 'HyphenMaxLines', 'HyphenMinPrefix', 'HyphenMinSuffix', 'HyphenMinWord', 'Hyphenate', 'LIndent', 'Language', 'Leading', 'LetterSpace', 'LineSpacing', 'Locked', 'MaxWordSpace', 'MinWordSpace', 'NextTag', 'NumFormat', 'NumberFont', 'NumAtEnd', 'OptWordSpace', 'Placement', 'PlacementStyle', 'RIndent', 'RunInDefaultPunct', 'SpAfter', 'SpBefore', 'TabStop', 'TopSepAtIndent', 'TopSepOffset', 'TopSeparator', 'UseNextTag', 'WithNext', 'WithPrev', ); # Define the set of font attributes. @MIF_FONT_ATTRS = ( 'Family', 'Var', 'Weight', 'Angle', 'PostScriptName', 'Size', 'Underlining', 'Overline', 'Strike', 'ChangeBar', 'Outline', 'Shadow', 'PairKern', 'Case', 'Position', 'DX', 'DY', 'DW', 'Locked', 'Separation', 'Color'); # This is the gap in points between the actual border and the # decoration for an area on a master page $_MIF_BORDER_GAP = 6; # This is the text flow id of the main text $_MIF_TEXTFLOW_MAIN = 9999; # This is where we start numbering our text flows from to ensure there # are no conflicts with those already in templates $_MIF_TEXTFLOW_START = 10000; # This is where we start numbering our objects from to ensure there # are no conflicts with those already in templates $_MIF_OBJ_REF_START = 11000; # This is where we start numbering TOC cross-references $_MIF_TOC_XREF_START = 2000; # The character mapping table (SDF -> MIF) %_MIF_CHAR = ( 'bullet', 'Bullet', 'c', '\xa9 ', 'cent', 'Cent', 'dagger', 'Dagger', 'doubledagger', 'DoubleDagger', 'emdash', 'EmDash', 'endash', 'EnDash', 'emspace', 'EmSpace', 'enspace', 'EnSpace', 'lbrace', '{', 'lbracket', '[', 'nbdash', 'HardHyphen', 'nbspace', 'HardSpace', 'nl', 'HardReturn', 'pound', 'Pound', 'r', '\xa8 ', 'rbrace', '}', 'rbracket', ']', 'tab', 'Tab', 'tm', '\xaa ', 'yen', 'Yen', # From pod2fm ... 'amp' => '&', # ampersand 'lt' => '<', # left chevron, less-than 'gt' => '\\>', # right chevron, greater-than 'quot' => '"', # double quote "Aacute" => "\\xe7 ", # capital A, acute accent "aacute" => "\\x87 ", # small a, acute accent "Acirc" => "\\xe5 ", # capital A, circumflex accent "acirc" => "\\x89 ", # small a, circumflex accent "AElig" => '\\xae ', # capital AE diphthong (ligature) "aelig" => '\\xbe ', # small ae diphthong (ligature) "Agrave" => "\\xcb ", # capital A, grave accent "agrave" => "\\x88 ", # small a, grave accent "Aring" => '\\x81 ', # capital A, ring "aring" => '\\x8c ', # small a, ring "Atilde" => '\\xcc ', # capital A, tilde "atilde" => '\\x8b ', # small a, tilde "Auml" => '\\x80 ', # capital A, dieresis or umlaut mark "auml" => '\\x8a ', # small a, dieresis or umlaut mark "Ccedil" => '\\x82 ', # capital C, cedilla "ccedil" => '\\x8d ', # small c, cedilla "Eacute" => "\\x83 ", # capital E, acute accent "eacute" => "\\x8e ", # small e, acute accent "Ecirc" => "\\xe6 ", # capital E, circumflex accent "ecirc" => "\\x90 ", # small e, circumflex accent "Egrave" => "\\xe9 ", # capital E, grave accent "egrave" => "\\x8f ", # small e, grave accent "Euml" => "\\xe8 ", # capital E, dieresis or umlaut mark "euml" => "\\x91 ", # small e, dieresis or umlaut mark "Iacute" => "\\xea ", # capital I, acute accent "iacute" => "\\x92 ", # small i, acute accent "Icirc" => "\\xeb ", # capital I, circumflex accent "icirc" => "\\x90 ", # small i, circumflex accent "Igrave" => "\\xe9 ", # capital I, grave accent "igrave" => "\\x93 ", # small i, grave accent "Iuml" => "\\xec ", # capital I, dieresis or umlaut mark "iuml" => "\\x95 ", # small i, dieresis or umlaut mark "Ntilde" => '\\x84 ', # capital N, tilde "ntilde" => '\\x96 ', # small n, tilde "Oacute" => "\\xee ", # capital O, acute accent "oacute" => "\\x97 ", # small o, acute accent "Ocirc" => "\\xef ", # capital O, circumflex accent "ocirc" => "\\x99 ", # small o, circumflex accent "Ograve" => "\\xf1 ", # capital O, grave accent "ograve" => "\\x98 ", # small o, grave accent "Oslash" => "\\xaf ", # capital O, slash "oslash" => "\\xbf ", # small o, slash "Otilde" => "\\xcd ", # capital O, tilde "otilde" => "\\x9b ", # small o, tilde "Ouml" => "\\x85 ", # capital O, dieresis or umlaut mark "ouml" => "\\x9a ", # small o, dieresis or umlaut mark "Uacute" => "\\xf2 ", # capital U, acute accent "uacute" => "\\x9c ", # small u, acute accent "Ucirc" => "\\xf3 ", # capital U, circumflex accent "ucirc" => "\\x9e ", # small u, circumflex accent "Ugrave" => "\\xf4 ", # capital U, grave accent "ugrave" => "\\x9d ", # small u, grave accent "Uuml" => "\\x86 ", # capital U, dieresis or umlaut mark "uuml" => "\\x9f ", # small u, dieresis or umlaut mark "yuml" => "\\xd8 ", # small y, dieresis or umlaut mark ); # Lookup table of colour values used by _MifMapColor() %_MIF_COLOR = ( 'black', 'Black', '000000', 'Black', 'white', 'White', 'ffffff', 'White', 'red', 'Red', 'ff0000', 'Red', 'green', 'Green', '008000', 'Green', 'blue', 'Blue', '0000ff', 'Blue', 'yellow', 'Yellow', 'ffff00', 'Yellow', 'magenta', 'Magenta', 'ff00ff', 'Magenta', 'cyan', 'Cyan', '00ffff', 'Cyan', ); # Lookup table of fill values %_MIF_FILL_CODE = ( 100, 0, 90, 1, 70, 2, 50, 3, 30, 4, 10, 5, 3, 6, 0, 15, ); # Lookup table of known index types %_MIF_INDEX_CODE = ( "standard", 2, "comment", 3, "subject", 4, "author", 5, ); # This is the numeric code matching the HardReturn character $_MIF_HARDRETURN_CODE = 10; # # >>_Description:: # {{Y:%_MIF_REF}} maps logical names to MIF reference names. # %_MIF_REF = ( 'table', 'ATbl', 'figure', 'AFrame', ); # Lookup table of title tags for objects %_MIF_TITLE_TAG = ( 'table', 'TT', 'figure', 'FT', ); # Lookup table of no-title tags for objects %_MIF_NOTITLE_TAG = ( 'table', 'TA', 'figure', 'FA', ); # The text at the end of each paragraph and its length $_MIF_PARA_END = " >\n> # end of Para"; $_MIF_PARA_END_LEN = length($_MIF_PARA_END); # # >>_Description:: # {{Y:%_MIF_DFLT_BOOK_ATTRS}} contains the default set of # book attributes. # %_MIF_DFLT_BOOK_ATTRS = ( "StartPageSide", "ReadFromFile", "PageNumbering", "Continue", "PgfNumbering", "Continue", "PageNumPrefix", "", "PageNumSuffix", "", "DefaultPrint", "Yes", "DefaultApply", "Yes", ); # Mapping table for logical variable names to Frame equivalents %_MIF_VAR_MAP = ( 'Running_H_F_1', 'Running H/F 1', 'Running_H_F_2', 'Running H/F 2', 'Running_H_F_3', 'Running H/F 3', 'Running_H_F_4', 'Running H/F 4', ); # Set of legal tab types %_MIF_TAB_TYPE = ( 'Left', 1, 'Center', 1, 'Right', 1, 'Decimal', 1, ); # Directive mapping table %_MIF_HANDLER = ( 'tuning', '_MifHandlerTuning', 'endtuning', '_MifHandlerEndTuning', 'table', '_MifHandlerTable', 'row', '_MifHandlerRow', 'cell', '_MifHandlerCell', 'endtable', '_MifHandlerEndTable', 'import', '_MifHandlerImport', 'inline', '_MifHandlerInline', 'output', '_MifHandlerOutput', 'object', '_MifHandlerObject', ); # Phrase directive mapping table %_MIF_PHRASE_HANDLER = ( 'char', '_MifPhraseHandlerChar', 'import', '_MifPhraseHandlerImport', 'inline', '_MifPhraseHandlerInline', 'variable', '_MifPhraseHandlerVariable', 'xref', '_MifPhraseHandlerXRef', 'pagenum', '_MifPhraseHandlerPageNum', 'pagecount', '_MifPhraseHandlerPageCount', 'paratext', '_MifPhraseHandlerParaText', 'paranum', '_MifPhraseHandlerParaNum', 'paranumonly', '_MifPhraseHandlerParaNumOnly', 'parashort', '_MifPhraseHandlerParaShort', 'paralast', '_MifPhraseHandlerParaLast', ); # Table states $_MIF_INTABLE = 1; $_MIF_INROW = 2; $_MIF_INCELL = 3; # Table row suffixes %_MIF_ROW_SUFFIX = ( 'Heading', 'H', 'Body', 'Body', 'Group', 'Body', 'Footing', 'F', ); # Parts in a book before numbering starts resetting %_MIF_FRONT_PART = ( 'front', 1, 'pretoc', 1, 'toc', 1, 'lof', 1, 'lot', 1, 'prechapter', 1, ); ##### Variables ##### # Counter to ensure stream processing functions are re-entrant $_mif_cnt = 0; # The current template stuff @_mif_template = (); %_mif_tpl_vars = (); %_mif_tpl_xrefs = (); %_mif_tpl_paras = (); %_mif_tpl_fonts = (); %_mif_tpl_tbls = (); %_mif_tpl_ctrls = (); # The current variables, cross-references, paragraph/font/table catalogs, # control settings, reference frames and generated lists %_mif_vars = (); %_mif_xrefs = (); %_mif_paras = (); %_mif_fonts = (); %_mif_tbls = (); %_mif_ctrls = (); %_mif_frames = (); %_mif_lists = (); %_mif_indexes = (); # Counter of Running H/F variables used $_mif_runninghf_cnt = 0; # Style usage counters. We don't bother keeping usage counts for # font styles because they are small to build and are referenced # in lots of places (e.g. autonumber fonts). %_mif_parastyle_used = (); %_mif_tblstyle_used = (); # The cover page name and rectangles $_mif_cover = ''; %_mif_cover_rect = (); # Buffers for collection of special objects @_mif_toc_list = (); @_mif_lof_list = (); @_mif_lof_list = (); @_mif_figure = (); @_mif_table = (); @_mif_textflows = (); @_mif_pages = (); $_mif_bodypage = ''; # Stacks for tables @_mif_tbl_start = (); @_mif_tbl_state = (); @_mif_tbl_wide = (); @_mif_tbl_id = (); @_mif_tbl_title = (); @_mif_tbl_style = (); @_mif_tbl_landscape = (); # Type of current row @_mif_row_type = (); # Alignment of current table cell $_mif_cell_align = ''; $_mif_cell_valign = ''; # Margin around current cell (and defaults for current table) $_mif_cell_lmargin = 0; $_mif_cell_tmargin = 0; $_mif_cell_rmargin = 0; $_mif_cell_bmargin = 0; $_mif_tbl_lmargin = 0; $_mif_tbl_tmargin = 0; $_mif_tbl_rmargin = 0; $_mif_tbl_bmargin = 0; # # >>_Description:: # {{Y:_mif_buffered_fname}} and {{Y:_mif_buffered_file}} contain the # filename and contents of the last album file fetched. # $_mif_buffered_fname = ''; @_mif_buffered_file = (); # Used to ensure text flows are unique $_mif_textflow_cnt = $_MIF_TEXTFLOW_START; # Used to ensure cross-reference targets are unique $_mif_xref_cnt = $_MIF_TOC_XREF_START; # Arrays of paragraph attribute information @_mif_paraattr_name = (); @_mif_paraattr_full = (); @_mif_paraattr_type = (); # Root objects for faster catalog building $_mif_pararoot_name = ''; %_mif_pararoot_attr = (); $_mif_fontroot_name = ''; %_mif_fontroot_attr = (); $_mif_tblroot_name = ''; %_mif_tblroot_attr = (); $_mif_frameroot_name = ''; %_mif_frameroot_attr = (); $_mif_listroot_name = ''; %_mif_listroot_attr = (); $_mif_indexroot_name = ''; %_mif_indexroot_attr = (); # Stacks of component file offsets and filenames @_mif_component_offset = (); @_mif_component_file = (); @_mif_component_type = (); # Table of component names and types @_mif_component_tbl = (); # Counter for building derived component names $_mif_component_cntr = 0; # Cursor in component type array $_mif_component_cursor= 0; ##### Routines ##### # # >>Description:: # {{Y:MifFormat}} is an SDF driver which outputs MIF. # sub MifFormat { local(*data) = @_; local(@result); local($msg_cursor, %msg_counts); # Init global variables/buffers $_mif_textflow_cnt = $_MIF_TEXTFLOW_START; $_mif_xref_cnt = $_MIF_TOC_XREF_START; %_mif_vars = (); %_mif_xrefs = (); %_mif_paras = (); %_mif_fonts = (); %_mif_tbls = (); %_mif_ctrls = (); %_mif_frames = (); %_mif_lists = (); %_mif_indexes = (); $_mif_runninghf_cnt = 0; %_mif_parastyle_used = (); %_mif_tblstyle_used = (); $_mif_cover = ''; %_mif_cover_rect = (); @_mif_toc_list = (); @_mif_lof_list = (); @_mif_lot_list = (); @_mif_figure = (); @_mif_table = (); @_mif_textflows = (); @_mif_pages = (); $_mif_bodypage = ''; @_mif_tbl_start = (); @_mif_tbl_wide = (); @_mif_tbl_state = (); @_mif_tbl_id = (); @_mif_tbl_title = (); @_mif_tbl_style = (); @_mif_tbl_landscape = (); @_mif_row_type = (); @_mif_component_offset = (); @_mif_component_file = (); @_mif_component_type = (); @_mif_component_tbl = (); $_mif_component_cntr = 0; $_mif_component_cursor = 0; # Initialise things for building a book, if necessary if ($SDF_USER'var{'MIF_BOOK_MODE'}) { @_mif_component_tbl = ('Part|Type'); } # Get the current message cursor - we skip the finalisation stuff # if errors are found $msg_cursor = &AppMsgNextIndex(); # Process the paragraphs @result = (); &_MifAddSection(*result, *data); # Save away any unclosed components while (@_mif_component_file) { &_MifHandlerOutput(*result, '-'); } # Finalise the output, provided that no errors/aborts/fatals were found %msg_counts = &AppMsgCounts($msg_cursor); if ($msg_counts{'error'} || $msg_counts{'abort'} || $msg_counts{'fatal'} ) { # do nothing } elsif ($SDF_USER'var{'MIF_BOOK_MODE'}) { @_mif_component_tbl = &TableParse(@_mif_component_tbl); @result = &_MifBookBuild(*_mif_component_tbl, $SDF_USER'var{'DOC_BASE'}); } else { @result = &_MifFinalise(*result); } # Return result return @result; } # # >>_Description:: # {{Y:_MifAddSection}} formats a block of SDF (@data) into MIF and # adds it to a buffer (@outbuf). # sub _MifAddSection { local(*outbuf, *data) = @_; # local(); local($prev_tag, %prev_attrs); local($para_tag, $para_text, %para_attrs); local($directive); # Process the paragraphs $prev_tag = ''; %prev_attrs = (); #print "data: ", join("<\n", @data), "<\n" if $mif_debug; while (($para_text, $para_tag, %para_attrs) = &SdfNextPara(*data)) { # handle directives if ($para_tag =~ /^__(\w+)$/) { $directive = $_MIF_HANDLER{$1}; if (defined &$directive) { &$directive(*outbuf, $para_text, %para_attrs); } else { &AppTrace("mif", 5, "ignoring internal directive '$1'"); } next; } # Add the paragraph &_MifParaAdd(*outbuf, $para_tag, $para_text, *para_attrs, $prev_tag, *prev_attrs); } # Do this stuff before starting next loop iteration continue { $prev_tag = $para_tag; %prev_attrs = %para_attrs; } } # # >>_Description:: # {{Y:_MifEscape}} escapes special characters in a MIF string. # If {{escape_space}} is true, space characters are also escaped. # sub _MifEscape { local(*text, $escape_space) = @_; # local(); local($orig_linematch_flag); $orig_linematch_flag = $*; $* = 1; $text =~ s/([\\>])/\\$1/g; $text =~ s/\t/\\t/g; $text =~ s/'/\\q/g; $text =~ s/`/\\Q/g; $text =~ s/ /\\ /g if $escape_space; $* = $orig_linematch_flag; } # # >>_Description:: # {{Y:_MifFmtMarker}} formats a mif marker. # sub _MifFmtMarker { local($prefix, $code, $text) = @_; local($marker); local($rest); # For long markers, make each entry a marker until the rest # is short enough to fit into one marker $marker = ''; while (length($text) > 255) { if ($text =~ /[^\\];/) { $rest = $`; $text = $'; &_MifEscape(*rest); $marker .= "$prefix\n" . "$prefix \n" . "$prefix> # end of Marker\n"; } else { # If we reach here, we are unable to split the marker text. # Therefore, the best we can do is take the first 255 chars # and let the user know. :-( $rest = substr($text, 255); $text = substr($text, 0, 255); &AppMsg("warning", "ignoring marker text - '$rest'"); } } # Handle the simple case &_MifEscape(*text); $marker .= "$prefix\n" . "$prefix \n" . "$prefix> # end of Marker\n"; return $marker; } # # >>_Description:: # {{Y:_MifParaAdd}} adds a paragraph. # sub _MifParaAdd { local(*result, $para_tag, $para_text, *para_attrs, $prev_tag, *prev_attrs) = @_; # local(); local($is_example, $para_fmt); local($para_override); local($para); local($hdg_level); local($id, $hlpinfo); local($index, $index_code); # Get the example flag $is_example = $SDF_USER'parastyles_category{$para_tag} eq 'example'; # After headings, use a FIRST tag instead of a normal tag if ($prev_tag =~ /^[HAP]\d$/ && $para_tag eq 'N' && $SDF_USER'parastyles_to{'FIRST'} ne '') { $para_tag = 'FIRST'; } # Get the Frame format name $para_fmt = $SDF_USER'parastyles_to{$para_tag}; $para_fmt = $para_tag if $para_fmt eq ''; $para_fmt .= 'NoTOC' if $para_attrs{'notoc'}; $_mif_parastyle_used{$para_fmt}++; # Inherit the alignment and margins from the table cell, if applicable if (@_mif_tbl_state) { if ($_mif_cell_align ne $para_attrs{'align'}) { $para_attrs{'align'} = $_mif_cell_align; } if ($_mif_cell_valign ne '') { $para_attrs{'mif.CellAlignment'} = $_mif_cell_valign; } # Get the cell margins, if necessary if ($_mif_cell_lmargin ne '' || $_mif_cell_tmargin ne '' || $_mif_cell_rmargin ne '' || $_mif_cell_bmargin ne '') { $_mif_cell_lmargin = $_mif_tbl_lmargin if $_mif_cell_lmargin eq ''; $_mif_cell_tmargin = $_mif_tbl_tmargin if $_mif_cell_tmargin eq ''; $_mif_cell_rmargin = $_mif_tbl_rmargin if $_mif_cell_rmargin eq ''; $_mif_cell_bmargin = $_mif_tbl_bmargin if $_mif_cell_bmargin eq ''; $para_attrs{'mif.CellMargins'} = "${_mif_cell_lmargin}pt ${_mif_cell_tmargin}pt " . "${_mif_cell_rmargin}pt ${_mif_cell_bmargin}pt"; $para_attrs{'mif.CellLMarginFixed'} = 1; $para_attrs{'mif.CellTMarginFixed'} = 1; $para_attrs{'mif.CellRMarginFixed'} = 1; $para_attrs{'mif.CellBMarginFixed'} = 1; } } # Get the format overrides &SdfAttrMap(*para_attrs, 'mif', *SDF_USER'paraattrs_to, *SDF_USER'paraattrs_map, *SDF_USER'paraattrs_attrs, $SDF_USER'parastyles_attrs{$para_tag}); $para_override = &_MifParaSdfAttr(*para_attrs, " "); # Build the paragraph header $para = "\n"; if ($para_override ne '') { $para .= " \n"; } $para .= " \n" . " \n" . " > # end of Marker\n"; # And we need this one for cross-references $para .= " \n" . " \n" . " > # end of Marker\n"; # And this one is for short text within a header/footer if ($para_attrs{"short"}) { $id = &_MifEscapeNewlink($para_attrs{"short"}); $para .= " \n" . " \n" . " > # end of Marker\n"; } $id = ''; } for $hlpinfo ('context', 'header', 'topic', 'window', 'endwindow') { $id = $para_attrs{"hlp.$hlpinfo"}; $id = &_MifEscapeNewlink($id); if ($id ne '') { $para .= " \n" . " \n" . " > # end of Marker\n"; } } # If this paragraph is in a generated list, add a cross reference, # unless we're in book mode unless ($SDF_USER'var{'MIF_BOOK_MODE'}) { if ($para_tag =~ /^[HAP](\d)$/) { $hdg_level = $1; if ($hdg_level <= $SDF_USER'var{'DOC_TOC'} && !$para_attrs{'notoc'}) { &_MifAddListXref(*para, $para_fmt, *_mif_toc_list, 'TOC'); } } elsif ($para_tag eq 'FT') { &_MifAddListXref(*para, $para_fmt, *_mif_lof_list, 'LOF'); } elsif ($para_tag eq 'TT') { &_MifAddListXref(*para, $para_fmt, *_mif_lot_list, 'LOT'); } } # Process index-related attributes $index = $para_attrs{"index"}; if ($index) { $index_code = $para_attrs{"index_type"}; if ($index_code eq '') { $index_code = 2; } elsif ($_MIF_INDEX_CODE{$index_code}) { $index_code = $_MIF_INDEX_CODE{$index_code}; } elsif ($index_code !~ /^\d+/) { &AppMsg("warning", "unknown index type '$index_code' - assuming standard"); $index_code = 2; } $para .= &_MifFmtMarker(" ", $index_code, $index); } # Indent examples, if necessary if ($is_example && $para_attrs{'in'}) { $para_text = " " x ($para_attrs{'in'} * 4) . $para_text; delete $para_attrs{'in'}; } # Add the paragraph body if ($para_attrs{'verbatim'}) { delete $para_attrs{'verbatim'}; &_MifEscape(*para_text, $is_example); $para .= " \n"; } else { $para .= &_MifParaText($para_text, $is_example); } # Build result if ($is_example && $para_tag eq $prev_tag && join('', %para_attrs) eq join('', %prev_attrs)) { &_MifParaAppend(*result, $para); } else { push(@result, $para . $_MIF_PARA_END); } } # # >>_Description:: # {{Y:_MifAddListXref}} adds a paragraph to a generated list. # sub _MifAddListXref { local(*para, $para_fmt, *list, $list_type) = @_; # local(); # Add the xref destination marker $para .= " \n" . " \n" . " > # end of Marker\n"; # And a special marker for HLP format $para .= " \n" . " \n" . " > # end of Marker\n"; # Add the xref to the global list $_mif_parastyle_used{"$para_fmt$list_type"}++; push(@list, "\n" . " \n" . " \n" . " > # end of XRef\n" . " >\n" . "> # end of Para"); # Update the global xref counter (which makes xref unique) $_mif_xref_cnt++; } # # >>_Description:: # {{Y:_MifInitTemplate}} initialises the global template variables. # sub _MifInitTemplate { # local() = @_; # local(); @_mif_template = (); %_mif_tpl_paras = (); %_mif_tpl_fonts = (); %_mif_tpl_vars = (); %_mif_tpl_xrefs = (); %_mif_tpl_tbls = (); %_mif_tpl_ctrls = (); } # # >>_Description:: # {{Y:_MifFetchTemplate}} loads a MIF template. # The following global variables are updated: # # * @_mif_template - each element contains one and only one main MIF object # * %_mif_tpl_vars - the set of variables # * %_mif_tpl_xrefs - the set of cross references # * %_mif_tpl_paras - the paragraph catalog # * %_mif_tpl_fonts - the font catalog # * %_mif_tpl_tbls - the table catalog (soon) # * %_mif_tpl_ctrls - the document control settings # sub _MifFetchTemplate { local($template_file) = @_; local($ok); local($strm, @rec); # Open the input stream $strm = sprintf("mif_s%d", $_mif_cnt++); open($strm, $template_file) || return 0; # Read the data. Objects taking a whole line get a record to # themselves. A slight variation of this is needed to cover the # opening line as it often has a trailing comment. #. Otherwise, we use a > at the start of a line to detect # the end of objects. line: while (<$strm>) { next line if /^#/; chop; if (/^\<.*\>$/ || /^\<.*\>\s*\#/) { push(@_mif_template, $_); } else { push(@rec, $_); if (/^\>/) { $text = join("\n", @rec); push(@_mif_template, $text); if ($rec[0] =~ /^\text: %d seconds\n", time - $igc_start; } elsif ($rec[0] =~ /^\>_Description:: # {{Y:_MifFetchAlbum}} handles fetching of album files. # sub _MifFetchAlbum { local($fname) = @_; local($ok, @file); local($strm); # Check if the file is buffered if ($fname eq $_mif_buffered_fname) { return (1, @_mif_buffered_file); } # Fetch the file $strm = sprintf("mif_s%d", $_mif_cnt++); $ok = open($strm, $fname); if ($ok) { @file = <$strm>; chop(@file); close($strm); } # Buffer the file $_mif_buffered_fname = $fname; @_mif_buffered_file = @file; # Return result return ($ok, @file); } # # >>_Description:: # {{Y:_MifVarsFromText}} converts text records (@recs) # that represent variable declarations in a MIF file to # a set of name value pairs. # sub _MifVarsFromText { local(*recs) = @_; local(%vars); local($line); local($name, $value); for $line (@recs) { if ($line =~ /VariableName\s+`(.+)'\>/) { $name = $1; } elsif ($line =~ /VariableDef\s+`(.+)'\>/) { $value = $1; $value =~ s/\\q/'/g; $value =~ s/\\Q/`/g; $value =~ s/\\t/\t/g; $value =~ s/\\([\\\>])/$1/g; $vars{$name} = $value; } } # Return result return %vars; } # # >>_Description:: # {{Y:_MifVarsToText}} converts {{%vars}} to the text string # that represents those declarations in a MIF file. # sub _MifVarsToText { local(*vars) = @_; local($text); local($var, $value); # Build the result $text = "\n" . " \n" . " > # end of VariableFormat\n"; } $text .= "> # end of VariableFormats"; # Return result return $text; } # # >>_Description:: # {{Y:_MifXRefsFromText}} converts text records (@recs) # that represent cross-reference declarations in a MIF file to # a set of name value pairs. # sub _MifXRefsFromText { local(*recs) = @_; local(%xrefs); local($line); local($name, $value); for $line (@recs) { if ($line =~ /XRefName\s+`(.+)'\>/) { $name = $1; } elsif ($line =~ /XRefDef\s+`(.+)'\>/) { $value = $1; $xrefs{$name} = $value; } } # Return result return %xrefs; } # # >>_Description:: # {{Y:_MifXRefsToText}} converts {{%xrefs}} to the text string # that represents those declarations in a MIF file. # sub _MifXRefsToText { local(*xrefs) = @_; local($text); local($xref, $value); # Build the result $text = "\n" . " \n" . " > # end of XRefFormat\n"; } $text .= "> # end of XRefFormats"; # Return result return $text; } # # >>_Description:: # {{Y:_MifParasFromText}} converts text records (@recs) that represent # paragraph definitions in a MIF file to a set of name value pairs. # Use {{Y:_MifAttrSplit}} and {{Y:_MifAttrSplit}} to convert # the value to and from an associative array. # sub _MifParasFromText { local(*recs) = @_; local(%result); local($line); local($name, %values); local($in_font, $in_tabstop, %tab, $tab); local($atname, $atvalue); $in_font = 0; $in_tabstop = 0; for $line (@recs) { $line =~ s/^\s+//; $line =~ s/\s+$//; if ($line =~ /^\$/) { $name = $1; %values = (); } elsif ($line =~ /^\<(\w+)\s+(.+)\>$/) { $atname = $1; $atvalue = &_MifAttrToText($2); if ($in_tabstop) { $tab{$atname} = $atvalue; } elsif($in_font) { $values{'Font' . substr($atname, 1)} = $atvalue; } else { $atname =~ s/^Pgf//; $values{$atname} = $atvalue; } } elsif ($line =~ /^\> # end of Pgf$/) { # Finalise the set of attributes delete $values{'FontTag'}; delete $values{'FontPostScriptName'}; delete $values{'NumTabs'}; $values{'TabStop'} = '' unless defined($values{'TabStop'}); # Store the attributes for this paragraph format $result{$name} = &_MifAttrJoin(*values); } elsif ($line =~ /^ # end of PgfFont$/) { $in_font = 0; } elsif ($line =~ /^ # end of TabStop$/) { $in_tabstop = 0; $tab{'TSType'} = '' if $tab{'TSType'} eq 'Left'; $tab{'TSLeaderStr'} = '' if $tab{'TSLeaderStr'} eq ' '; $tab = join('/', @tab{'TSX', 'TSType', 'TSLeaderStr'}); $tab =~ s/\/+$//; if (defined($values{'TabStop'})) { $values{'TabStop'} .= "," . $tab; next; } $values{'TabStop'} = $tab; } } # Return result return %result; } # # >>_Description:: # {{Y:_MifParasToText}} converts {{%paras}} to the text string # that represents those declarations in a MIF file. # sub _MifParasToText { local(*paras) = @_; local($text); local(@styles); local($name, %attr); # Build the attribute information arrays &_MifBuildAttrInfo(); # Decide on the styles to output @styles = $SDF_USER'var{'MIF_ALL_STYLES'} ? sort keys %paras : sort keys %_mif_parastyle_used; # Build the result $text = "\n", &_MifParaMifAttr(*attr, ' '), " > # end of Pgf\n"); } $text .= "> # end of PgfCatalog"; # Return result return $text; } # # >>_Description:: # {{Y:_MifFontsFromText}} converts text records (@recs) that represent # font definitions in a MIF file to a set of name value pairs. # Use {{Y:_MifAttrJoin}} and {{Y:_MifAttrSplit}} to convert # the value to and from an associative array. # sub _MifFontsFromText { local(*recs) = @_; local(%result); local($line); local($name, %values); for $line (@recs) { $line =~ s/^\s+//; $line =~ s/\s+$//; if ($line =~ /^$/) { $name = $1; %values = (); } elsif ($line =~ /^$/) { $values{$1} = &_MifAttrToText($2); } elsif ($line =~ /^\> # end of Font$/) { delete $values{'PostScriptName'}; $result{$name} = &_MifAttrJoin(*values); } } # Return result return %result; } # # >>_Description:: # {{Y:_MifFontsToText}} converts {{%fonts}} to the text string # that represents those declarations in a MIF file. # sub _MifFontsToText { local(*fonts) = @_; local($text); local($name, %attr); # Build the result $text = ">_Description:: # {{Y:_MifTblsFromText}} converts text records (@recs) that represent # table format definitions in a MIF file to a set of name value pairs. # Use {{Y:_MifAttrJoin}} and {{Y:_MifAttrSplit}} to convert # the value to and from an associative array. # sub _MifTblsFromText { local(*recs) = @_; local(%result); local($line); local($name, %values); for $line (@recs) { $line =~ s/^\s+//; $line =~ s/\s+$//; if ($line =~ /^$/) { $name = $1; %values = (); $nested = 0; } elsif ($line =~ /^$/) { $values{$1} = &_MifAttrToText($2) unless $nested; } elsif ($line =~ /^ # end of Tbl(Column|TitlePgf1)$/) { $nested = 0; } elsif ($line =~ /^\> # end of TblFormat$/) { $result{$name} = &_MifAttrJoin(*values); } } # Return result return %result; } # # >>_Description:: # {{Y:_MifTblsToText}} converts {{%tbls}} to the text string # that represents those declarations in a MIF file. # sub _MifTblsToText { local(*tbls) = @_; local($text); local(@styles); local($name, %attr); # Decide on the styles to output @styles = $SDF_USER'var{'MIF_ALL_STYLES'} ? sort keys %tbls : sort keys %_mif_tblstyle_used; # Build the result $text = ">_Description:: # {{Y:_MifTblFormat}} formats a table format name and # set of attributes (%attr) into MIF. # {{prefix}} is a string of spaces to put at the front of each line. # sub _MifTblFormat { local($name, $prefix, *attr) = @_; # local($result); local(@text); local($id, $type, $value); # Build the header @text = ("$prefix"); # Add the attributes for $id (sort keys %attr) { $type = $SDF_USER'tableparams_type{"mif.$id"}; $value = &_MifAttrFromText($attr{$id}, $type); push(@text, " "); } # Add a dummy table column push(@text, " ", " ", " ", " > # end of TblColumnH", " ", " > # end of TblColumnBody", " ", " > # end of TblColumnF", " > # end of TblColumn"); # Add the footer push(@text, "> # end of TblFormat\n"); # Return result return join("\n$prefix", @text); } # # >>_Description:: # {{Y:_MifCtrlsFromText}} converts text records (@recs) that represent # document contorl settings in a MIF file to a set of name value pairs. # sub _MifCtrlsFromText { local(*recs) = @_; local(%result); local($line); for $line (@recs) { $line =~ s/^\s+//; $line =~ s/\s+$//; if ($line =~ /^<(\w+)\s+(.+)\>$/) { $result{$1} = &_MifAttrToText($2); } } # Return result return %result; } # # >>_Description:: # {{Y:_MifCtrlsToText}} converts {{%ctrls}} to the text string # that represents those settings in a MIF file. # sub _MifCtrlsToText { local(*ctrls) = @_; local($text); local($name, $var_name, $type, $value); # Build the result $text = ">_Description:: # {{Y:_MifAttrSplit}} split a string of attributes into an associative array. # The string format is a null-separated list of "name=value" strings. # sub _MifAttrSplit { local($str) = @_; local(%attr); local($nv); for $nv (split(/\000/, $str)) { $nv =~ /\=/; $attr{$`} = $'; } # Return result %attr; } # # >>_Description:: # {{Y:_MifAttrJoin}} joins an associative array into a string of attributes. # (A "reference" to the array is passed for performance.) # The string format is a null-separated list of "name=value" strings. # sub _MifAttrJoin { local(*attr) = @_; local($str); local($name, $value); # Build the string $str .= "$name=$value\000" while ($name, $value) = each %attr; # Return result $str; } # # >>_Description:: # {{Y:_MifAttrToText}} formats a MIF attribute into text. # As this routine can have a major impact on performance, # the implementation favors performance over readability. # sub _MifAttrToText { local($mif) = @_; # local($text); # Trim leading/trailing whitespace $mif =~ s/^\s+//; $mif =~ s/\s+$//; # Convert boolean values to numeric $mif = 0 if $mif eq 'No'; # Trim surrounding quotes, if any $mif =~ s/^\`(.*)\'$/$1/; # Return result $mif; } # # >>_Description:: # {{Y:_MifAttrFromText}} formats an attribute into MIF. # {{type}} can be one of the following: # # * {{boolean}} - output is Yes or No # * {{string}} - output is `data' # * {{tabstop}} - output is a tabstop record # * {{keyword}} - output has a trailing space (matches MIF conventions) # # Other types return the value as it is. If the type is unknown: # # * 0 is converted to No # * other values are returned as is. # # This algorithm minimises the problems associated with unknown attributes. # # As this routine can have a major impact on performance, # the implementation favors performance over readability. # sub _MifAttrFromText { local($value, $type) = @_; # local($text); # Handle special cases return $value ? 'Yes ' : 'No ' if $type =~ /^b/; # boolean return "`$value'" if $type =~ /^s/; # string return "$value " if $type =~ /^k/; # keyword return &_MifTabFromText($value) if $type =~ /^t/; # tabstop return $value if $type =~ /^\w/; # other # Otherwise, return the value as is, or No return $value eq '0' ? 'No ' : $value; } # # >>_Description:: # {{Y:_MifTabFromText}} formats a tabstop attribute into MIF. # sub _MifTabFromText { local($value) = @_; local($text); local(@tabs, $tab, $tsx, $tstype, $tsleader); @tabs = split(/\s*,\s*/, $value); $text = ''; for $tab (@tabs) { $text .= ">\n >_Description:: # {{Y:_MifParaSdfAttr}} formats a set of SDF paragraph attributes into MIF. # {{prefix}} is a string of spaces to put at the front of each line. # sub _MifParaSdfAttr { local(*attrs, $prefix) = @_; local($mif); local($attr, $value, $type, $fm_prefix, $font); for $attr (sort keys %attrs) { # get the attribute value & type $value = $attrs{$attr}; $type = $SDF_USER'paraattrs_type{$attr}; # Get the Frame prefix $fm_prefix = $SDF_USER'phraseattrs_name{$attr} ? 'F' : 'Pgf'; # Check it's a Frame attribute next unless $attr =~ s/^mif\.//; # Map to the MIF name $attr = "$fm_prefix$attr" unless ($attr =~ /^HyphenM/ || $attr eq 'TabStop'); # format the value $value = &_MifAttrFromText($value, $type); # build the result, separating the font attributes for now if ($fm_prefix eq 'F') { $font .= "$prefix <$attr $value>\n"; } else { $mif .= "$prefix<$attr $value>\n"; } } # Combine normal and font attributes $mif .= "$prefix\n$font$prefix>\n" if $font; # Return result $mif; } # # >>_Description:: # {{Y:_MifBuildAttrInfo}} builds the table of paragraph attribute # information. Each record contains the the attribute name & type, # separated by a NULL character. # sub _MifBuildAttrInfo { # local() = @_; # local(); local($name, $short_name, $new_name); # If the work has already been done, don't bother doing it again return if @_mif_paraattr_name; # Add the attribute information for $name (sort keys %SDF_USER'paraattrs_type) { next unless $name =~ /^mif\./; # Skip attributes we don't weant to output next if $name eq 'mif.NumTabs'; next if $name eq 'mif.component'; # Convert the name $short_name = $name; $short_name =~ s/^mif\.//; if ($short_name =~ /^Font/) { $new_name = "F" . $'; } elsif ($short_name =~ /^HyphenM|^TabS/) { $new_name = $short_name; } else { $new_name = "Pgf" . $short_name; } # Store the information push(@_mif_paraattr_name, $short_name); push(@_mif_paraattr_full, $new_name); push(@_mif_paraattr_type, $SDF_USER'paraattrs_type{$name}); } } # # >>_Description:: # {{Y:_MifParaMifAttr}} formats a set of MIF paragraph attributes into MIF. # {{prefix}} is a string of spaces to put at the front of each line. # sub _MifParaMifAttr { local(*attrs, $prefix) = @_; local($mif); local($i, $attr, $value, $fullname, $type, $font); # This routine has a big impact on performance, so we # get the list of names from a prebuilt array, rather than by # sorting the keys of %attrs. Likewise, the fullname and type # come from prebuilt arrays, rather than from assocative arrays # indexed on attribute name, to improve performance. for ($i = 0; $i <= $#_mif_paraattr_name; $i++) { # get the attribute information $attr = $_mif_paraattr_name[$i]; $fullname = $_mif_paraattr_full[$i]; $type = $_mif_paraattr_type[$i]; # format the value $value = &_MifAttrFromText($attrs{$attr}, $type); # build the result, separating the font attributes for now # and ignoring empty tab stops ## Note that we explicitly test for the most common case first ## in order to improve performance. #if ($fullname =~ /^P/) { # $mif .= "$prefix<$fullname $value>\n"; #} #elsif ($fullname =~ /^F/) { if ($fullname =~ /^F/) { $font .= "$prefix <$fullname $value>\n"; } elsif ($fullname =~ /^T/ && $value eq '') { next; } else { $mif .= "$prefix<$fullname $value>\n"; } } # Combine normal and font attributes $mif .= "$prefix\n$font$prefix>\n" if $font; # Return result $mif; } # # >>_Description:: # {{Y:_MifFontFormat}} formats a MIF font specification from a # tag and set of MIF attributes. # sub _MifFontFormat { local($tag, $prefix, %attr) = @_; local($mif); local($attr); local($short_name); local($type, $value); # Init things $mif = "$prefix\n"; # Process the attributes for $attr (sort keys %attr) { # Get the short name $short_name = $attr; $short_name =~ s/^mif\.//; # get the attribute type and value $type = $SDF_USER'phraseattrs_type{"mif.$short_name"}; $value = $attr{$attr}; # For MIF font definitions, an empty value implies "as-is" # and as-is attributes are not including in the definition next if $value eq ''; # format the value $value = &_MifAttrFromText($value, $type); # build the result $mif .= "$prefix \n"; } $mif .= "$prefix>\n"; # Return result return $mif; } # # >>_Description:: # {{Y:_MifCharFont}} formats a MIF font specification from a # tag and set of SDF attributes. # sub _MifCharFont { local($tag, $prefix, %attr) = @_; local($mif); local($attr); local($type, $value); # Init things $mif = "$prefix\n"; # Process the attributes for $attr (sort keys %attr) { # get the attribute type and value $type = $SDF_USER'phraseattrs_type{$attr}; $value = $attr{$attr}; # Check it's a Frame attribute and map it to a MIF name next unless $attr =~ s/^mif\./F/; # format the value $value = &_MifAttrFromText($value, $type); # build the result $mif .= "$prefix <$attr $value>\n"; } $mif .= "$prefix>\n"; # Return result return $mif; } # # >>_Description:: # {{Y:_MifParaText}} converts SDF text into MIF "paragraph lines". # sub _MifParaText { local($para_text, $is_example) = @_; local($para); local($state); local($sect_type, $char_tag, $text, %sect_attrs); local(@char_fonts); local($char_font, $id, $index, $index_code); local($directive); # Handle blank lines $para_text = " " if $para_text eq ''; # Process the text $para = ''; while (($sect_type, $text, $char_tag, %sect_attrs) = &SdfNextSection(*para_text, *state)) { # Escape any special characters if ($sect_type eq 'phrase') { ($text) = &SDF_USER'ExpandLink($text) if $char_tag eq 'L'; &_MifEscape(*text, $is_example); } elsif ($sect_type eq 'string') { &_MifEscape(*text, $is_example); } # Build the paragraph if ($sect_type eq 'string') { # Convert hyphens to hard-hyphens, if in a phrase $text =~ s/\-/\\x15 /g if @char_fonts; $para .= " \n"; } elsif ($sect_type eq 'phrase') { # Map colors to safe values if ($sect_attrs{'color'} ne '') { my $color = &_MifMapColor($sect_attrs{'color'}); if ($color ne '') { $sect_attrs{'color'} = $color; } else { delete $sect_attrs{'color'}; } } # Process formatting attributes &SdfAttrMap(*sect_attrs, 'mif', *SDF_USER'phraseattrs_to, *SDF_USER'phraseattrs_map, *SDF_USER'phraseattrs_attrs, $SDF_USER'phrasestyles_attrs{$char_tag}); $char_font = &_MifCharFont($SDF_USER'phrasestyles_to{$char_tag}, " ", %sect_attrs); push(@char_fonts, $char_font); $para .= $char_font; # Process hypertext-related attributes $id = &_MifEscapeNewlink($sect_attrs{"id"}); if ($id) { # We need this one for hypertext $para .= " \n" . " \n" . " > # end of Marker\n"; # And we need this one for cross-references $para .= " \n" . " \n" . " > # end of Marker\n"; } &_MifAddLink(*para, $sect_attrs{'jump'}); $id = &_MifEscapeNewlink($sect_attrs{"hlp.popup"}); if ($id ne '') { $para .= " \n" . " \n" . " > # end of Marker\n"; } # Process index-related attributes $index = $sect_attrs{"index"}; if ($index) { $index_code = $sect_attrs{"index_type"}; if ($index_code eq '') { $index_code = 2; } elsif ($_MIF_INDEX_CODE{$index_code}) { $index_code = $_MIF_INDEX_CODE{$index_code}; } elsif ($index_code !~ /^\d+/) { &AppMsg("warning", "unknown index type '$index_code' - assuming standard"); $index_code = 2; } $para .= &_MifFmtMarker(" ", $index_code, $index); } # Convert hyphens to hard-hyphens $text =~ s/\-/\\x15 /g; # Convert spaces to non-breaking spaces, if necessary $text =~ s/ /\\x11 /g if $char_tag eq 'S'; # Add the text for this phrase $para .= " \n"; } elsif ($sect_type eq 'phrase_end') { pop(@char_fonts); $char_font = $char_fonts[$#char_fonts]; $para .= $char_font ne '' ? $char_font : " \n > # end of Font\n"; } elsif ($sect_type eq 'special') { $directive = $_MIF_PHRASE_HANDLER{$char_tag}; if (defined &$directive) { &$directive(*para, $text, %sect_attrs); } else { &AppMsg("warning", "ignoring special phrase '$1' in MIF driver"); } } } # Return result return $para; } # # >>_Description:: # {{Y:_MifMapColor}} maps Web (CSS) colors to MIF colors. # sub _MifMapColor { local($color) = @_; local($mif_color); # Ignore leading # on rrggbb value, if any $color =~ s/^#//; # For now, if it's not in the lookup table, ignore it $mif_color = $_MIF_COLOR{"\L$color"}; # Return result return $mif_color; } # # >>_Description:: # {{Y:_MifAddLink}} adds link information for a paragraph. # sub _MifAddLink { local(*para, $link) = @_; # local(); if ($link) { $link = &_MifLink($link, $SDF_USER'var{'MIF_EXT'}); $para .= " \n" . " \n" . " > # end of Marker\n"; } } # # >>_Description:: # {{Y:_MifFinalise}} generates a MIF file. # If a template has been loaded, the result is a merge of the buffered # MIF objects with the current template. Otherwise, the output is the # buffered MIF objects prepended onto the text records. (In the latter # case, the generated MIF must be imported into a template to produce # the final document.) If the output is a component in a book, # {{component_type}} should be set accordingly (e.g. FRONT, TOC, # CHAPTER, etc.) # sub _MifFinalise { local(*text, $component_type) = @_; local(@out_result); local($pwidth, $pheight); local($component_prefix); local(%offset, $old_match_rule); local(%merged_ctrls, %merged_vars, %merged_xrefs); local(%merged_paras, %merged_fonts, %merged_tbls); local($mainflow); # Process document control settings &_MifProcessControls(*SDF_USER'var, $component_type); # Get the page width and height $pwidth = $SDF_USER'var{'DOC_PAGE_WIDTH'}; $pheight = $SDF_USER'var{'DOC_PAGE_HEIGHT'}; # Generate the master pages $component_prefix = $component_type eq '' ? '' : $component_type . "_"; &_MifAddMasterPage('First', $component_prefix . "FIRST", $pwidth, $pheight, $_MIF_TEXTFLOW_MAIN); &_MifAddMasterPage('Right', $component_prefix . "RIGHT", $pwidth, $pheight); if ($SDF_USER'var{'DOC_TWO_SIDES'}) { &_MifAddMasterPage('Left', $component_prefix . "LEFT", $pwidth, $pheight); } # Generate the reference pages &_MifAddRefPages($component_type); # Add the generated lists (Table of Contents, etc.) unless this is # a part of a book &_MifAddLists(*text) if $component_type eq ''; # # Build the import table. Note that each record in the # import table contains a single MIF main statement. # As we go, we also build an offset table which is # required by MifMerge so it can merge the import # table with the nominated template. # @out_result = (""); if (%_mif_ctrls) { %merged_ctrls = %_mif_tpl_ctrls; @merged_ctrls{keys %_mif_ctrls} = values %_mif_ctrls; push(@out_result, &_MifCtrlsToText(*merged_ctrls)); $offset{'Document'} = $#out_result; } if (%_mif_vars) { %merged_vars = %_mif_tpl_vars; @merged_vars{keys %_mif_vars} = values %_mif_vars; push(@out_result, &_MifVarsToText(*merged_vars)); $offset{'VariableFormats'} = $#out_result; } if (%_mif_xrefs) { %merged_xrefs = %_mif_tpl_xrefs; @merged_xrefs{keys %_mif_xrefs} = values %_mif_xrefs; push(@out_result, &_MifXRefsToText(*merged_xrefs)); $offset{'XRefFormats'} = $#out_result; } if (%_mif_paras) { $igc_start = time; %merged_paras = %_mif_tpl_paras; @merged_paras{keys %_mif_paras} = values %_mif_paras; push(@out_result, &_MifParasToText(*merged_paras)); #printf STDERR "text->para: %d seconds\n", time - $igc_start; $offset{'PgfCatalog'} = $#out_result; } if (%_mif_fonts) { $igc_start = time; %merged_fonts = %_mif_tpl_fonts; @merged_fonts{keys %_mif_fonts} = values %_mif_fonts; push(@out_result, &_MifFontsToText(*merged_fonts)); #printf STDERR "text->font: %d seconds\n", time - $igc_start; $offset{'FontCatalog'} = $#out_result; } if (%_mif_tbls) { $igc_start = time; %merged_tbls = %_mif_tpl_tbls; @merged_tbls{keys %_mif_tbls} = values %_mif_tbls; push(@out_result, &_MifTblsToText(*merged_tbls)); #printf STDERR "text->tbl: %d seconds\n", time - $igc_start; $offset{'TblCatalog'} = $#out_result; } if (@_mif_figure) { push(@out_result, join("\n", ' # end of AFrames' )); $offset{'AFrames'} = $#out_result; } if (@_mif_table) { push(@out_result, join("\n", ' # end of Tbls' )); $offset{'Tbls'} = $#out_result; } if (@_mif_pages) { push(@out_result, join("\n", @_mif_pages, $_mif_bodypage)); $offset{'Page'} = $#out_result; } # Build the main text flow: # * add the header # * patch in the TextRect ID for the main flow # * ensure that nested lines are indented # * add the footer. $mainflow = join("\n", "", "", "", "", "", "", " # end of Notes", @text); $old_match_rule = $*; $* = 1; $mainflow =~ s/\/; $mainflow =~ s/\n/\n /g; $mainflow .= "\n> # end of TextFlow"; $* = $old_match_rule; # Add the text flows to the import table push(@out_result, join("\n", @_mif_textflows, $mainflow)); $offset{'TextFlow'} = $#out_result; # If we're building a book, reset the buffers ready for the next part if ($component_type ne '') { @_mif_figure = (); @_mif_table = (); @_mif_pages = (); @_mif_textflows = (); } # Merge with the current template, if necessary if (scalar(@_mif_template) > 0) { @out_result = &_MifMerge(*_mif_template, *out_result, %offset); } # Return result push(@out_result, "# End of MIFFile"); return @out_result; } # # >>_Description:: # {{Y:_MifProcessControls}} checks the document control variables # and sets the MIF control settings appropriately. # sub _MifProcessControls { local(*vars, $component_type) = @_; # local(); local($page_size, $page_width, $page_height); local($sdf_tag, $mif_tag, $prefix); # Set the page size $page_size = $sdf_pagesize{$vars{'OPT_PAGE_SIZE'}}; if ($page_size ne '') { ($page_width, $page_height) = split(/\000/, $page_size, 2); } else { # Custom size ($page_width, $page_height) = split(/x/, $vars{'OPT_PAGE_SIZE'}, 2); } $page_width .= "pt" if $page_width =~ /^[\d\.]+$/; $page_width .= "pt" if $page_width =~ /^[\d\.]+$/; $_mif_ctrls{'DPageSize'} = "$page_width $page_height"; # Set the number of sides $_mif_ctrls{'DTwoSides'} = $vars{'DOC_TWO_SIDES'} || $vars{'MIF_TWO_SIDES'}; # If numbering per section is enabled, adjust the 'Current Page #' # variable definition, if necessary if ($vars{'MIF_BOOK_MODE'} && $vars{'OPT_NUMBER_PER_COMPONENT'}) { $_mif_ctrls{'DPageNumStyle'} = 'Arabic'; if ($component_type eq 'CHAPTER') { $sdf_tag = $vars{'OPT_COMPONENT_COVER'} ? 'H1NUM' : 'H1'; $mif_tag = $SDF_USER'parastyles_to{$sdf_tag}; $prefix = "<\$paranumonly[$mif_tag]>"; } elsif ($component_type eq 'APPENDIX') { $sdf_tag = $vars{'OPT_COMPONENT_COVER'} ? 'A1NUM' : 'A1'; $mif_tag = $SDF_USER'parastyles_to{$sdf_tag}; $prefix = "<\$paranumonly[$mif_tag]>"; } elsif ($component_type eq 'IX') { $prefix = "Index"; } elsif (! $_MIF_FRONT_PART{"\L$component_type"}) { $prefix = "\u\L$component_type"; } else { # Use roman numerals for page numbers of front matter $_mif_ctrls{'DPageNumStyle'} = 'LCRoman'; } if ($prefix) { $_mif_vars{'Current Page #'} = "$prefix-<\$curpagenum>"; } else { $_mif_vars{'Current Page #'} = "<\$curpagenum>"; } } } # # >>_Description:: # {{Y:_MifAddMasterPage}} adds a master page to the internal buffers. # {{name}} is either Left, Right or a 'user' name. # (In the latter case, the MIF type is implicitly 'OtherMasterPage'.) # {{sdf_type}} is the SDF page type (e.g. FIRST, FRONT_RIGHT). # {{page_width}} and {{page_height}} are the page width and height # in points. # The text flows for the header and footer, if any, are added to # internal buffers used to generate the final document. # If you want a matching body page for this master page, then set # {{bodypage_id}} to the id of the TextRect within that body page. # sub _MifAddMasterPage { local($name, $sdf_type, $page_width, $page_height, $bodypage_id) = @_; # local(); local(@page); local($mif_type); local($left, $width); local($h_top, $h_height); local($m_top, $m_height); local($f_top, $f_height); local($prefix); local($sh_width, $sh_gap); local($col_count, $col_gap); local(@body); local(@text, $id); local($background); # Build the header if ($name eq 'Left' || $name eq 'Right') { $mif_type = $name . "MasterPage"; } else { $mif_type = "OtherMasterPage"; } @page = ( "", " ", " "); # Calculate the left & width (used by all rectangles on the page) $left = ($name eq 'Left') ? &SdfVarPoints("OPT_MARGIN_OUTER") : &SdfVarPoints("OPT_MARGIN_INNER"); $width = $page_width - &SdfVarPoints("OPT_MARGIN_OUTER") - &SdfVarPoints("OPT_MARGIN_INNER"); # Calculate the tops and heights $h_top = &SdfVarPoints("OPT_MARGIN_TOP"); $h_height = &SdfPageInfo($sdf_type, "HEADER_HEIGHT", "pt"); $f_height = &SdfPageInfo($sdf_type, "FOOTER_HEIGHT", "pt"); $f_top = $page_height - $f_height - &SdfVarPoints("OPT_MARGIN_BOTTOM"); $m_top = $h_top + $h_height + &SdfPageInfo($sdf_type, "HEADER_GAP", "pt"); $m_height = $f_top - $m_top - &SdfPageInfo($sdf_type, "FOOTER_GAP", "pt"); # Get the sidehead and column details $prefix = $sdf_type =~ /^IX/ ? 'OPT_IX' : 'OPT'; $sh_width = &SdfVarPoints("${prefix}_SIDEHEAD_WIDTH"); $sh_gap = &SdfVarPoints("${prefix}_SIDEHEAD_GAP"); $col_count = $SDF_USER'var{"${prefix}_COLUMNS"}; $col_gap = &SdfVarPoints("${prefix}_COLUMN_GAP"); # Add the main section @text = (); $id = &_MifAddTextFlow(*text, 'A'); &_MifAddTextArea(*page, $id, $left, $m_top, $width, $m_height, $sh_width, $sh_gap, &SdfPageInfo($sdf_type, "MAIN_BORDER"), $col_count, $col_gap); if ($bodypage_id ne '') { @body = ( "", " "); &_MifAddTextArea(*body, $bodypage_id, $left, $m_top, $width, $m_height, $sh_width, $sh_gap, '', $col_count, $col_gap); push(@body, "> # end of Page"); $_mif_bodypage = join("\n", @body); } # Add the header, if any if ($h_height) { @text = split("\n", &SdfPageInfo($sdf_type, "HEADER", "macro")); @text = ('HEADER:') unless @text; $id = &_MifAddTextFlow(*text, ''); &_MifAddTextArea(*page, $id, $left, $h_top, $width, $h_height, $sh_width, $sh_gap, &SdfPageInfo($sdf_type, "HEADER_BORDER")); } # Add the footer, if any if ($f_height) { @text = split("\n", &SdfPageInfo($sdf_type, "FOOTER", "macro")); @text = ('FOOTER:') unless @text; #$mif_debug = 1; $id = &_MifAddTextFlow(*text, ''); $mif_debug = 0; &_MifAddTextArea(*page, $id, $left, $f_top, $width, $f_height, $sh_width, $sh_gap, &SdfPageInfo($sdf_type, "FOOTER_BORDER")); } # Add background objects, if any $background = &SdfPageInfo($sdf_type, "BACKGROUND"); if ($background ne '') { &_MifAddPageBackground(*page, $background, $background); } # Add the object footer push(@page, "> # end of Page"); # Add the page to the internal buffers push(@_mif_pages, join("\n", @page)); } # # >>_Description:: # {{Y:_MifAddTextFlow}} adds a text flow to the internal buffers. # {{@text}} is the sdf for the text in the text flow, # unless {{mif}} is true, in which case {{@text}} is assumed to be MIF. # {{tag}} is the tag of the text flow, if any. # The {{id}} of the text flow added is returned. # sub _MifAddTextFlow { local(*text, $tag, $mif) = @_; local($id); local(@hdr, @flow); local($textflow, $old_match_rule); # Get the next text flow id $id = $_mif_textflow_cnt++; # Build the text flow header @hdr = ("", " "); } # Convert the SDF to a MIF, if necessary if ($mif) { @flow = @text; } else { @flow = (); &_MifAddSection(*flow, *text); } # Convert to a text flow if (@flow) { $textflow = join("\n", @flow); $old_match_rule = $*; $* = 1; $textflow =~ s/\/; $textflow =~ s/\n/\n /g; $textflow .= "\n> # end of TextFlow"; $* = $old_match_rule; } # If nothing was generated, build the text flow with a dummy paragraph else { $textflow = join("\n", " ", " > # end of ParaLine", " > # end of Para", "> # end of TextFlow"); } # Add the text flow to the internal buffers push(@_mif_textflows, join("\n", @hdr, $textflow)); # Return result return $id; } # # >>_Description:: # {{Y:_MifAddTextArea}} adds a text area to a master or reference page. # {{@page}} is the MIF page data (so far). # {{id}} is the id of the text flow for this area. # {{left}}, {{top}}, {{width}} and {{height}} give the # rectangle's position. {{shwidth}} and {{shgap}} give # the sidehead width and gap respectively. # {{border}} is a comma-separated list of # attributes which collectively describe the border. # The format of each attribute is name[=value]. # The supported attributes are: # # * {{top}} - a line above the area # * {{bottom}} - a line below the area # * {{box}} - a box around the area # * {{radius}} - for a box, the radius of the corner. # # For {{top}}, {{bottom}} and {{box}}, the value of the # attribute is the line width in points. # sub _MifAddTextArea { local(*page, $id, $left, $top, $width, $height, $shwidth, $shgap, $border, $colcnt, $colgap) = @_; # local(); local(%border, $nv, $name, $value); local($left2, $top2, $width2, $height2); local($right, $bottom); # Convert the border attribute to a set of name-value pairs %border = (); for $nv (split(/\s*,\s*/, $border)) { if ($nv =~ /\=/) { $name = $`; $value = $'; } else { $name = $nv; $value = 1; } $border{$name} = $value; } # Add the border, if any for $name (sort keys %border) { $value = $border{$name}; if ($name eq 'top') { # put the line just above the actual border $top2 = $top - $_MIF_BORDER_GAP; $right = $left + $width; push(@page, &_MifLine($left, $top2, $right, $top2, " ", 0, $value)); } elsif ($name eq 'bottom') { # put the line just below the actual border $right = $left + $width; $bottom = $top + $height + $_MIF_BORDER_GAP; push(@page, &_MifLine($left, $bottom, $right, $bottom, " ", 0, $value)); } elsif ($name eq 'box') { # put the rectangle just around the actual border $left2 = $left - $_MIF_BORDER_GAP; $top2 = $top - $_MIF_BORDER_GAP; $width2 = $width + $_MIF_BORDER_GAP * 2; $height2 = $height + $_MIF_BORDER_GAP * 2; push(@page, &_MifRectangle($left2, $top2, $width2, $height2, $border{'radius'}, " ", 0, $value)); } } # Add the text rectangle. Note that we explicitly do this AFTER # the border stuff so that MIF attribute inheritance will work # correctly for background objects (if any). $colcnt = 1 if $colcnt < 1; $colgap = 0 if $colgap eq ''; push(@page, " ", " ", " ", " ", " ", " ", " ", " ", " ", " > # end of TextRect"); } # # >>_Description:: # {{Y:_MifLine}} creates a MIF line object. # sub _MifLine { local($x1, $y1, $x2, $y2, $prefix, $pen, $pen_width, $color) = @_; # local($result); local(@line); # Apply defaults $pen = 0 if $pen eq ''; $pen_width = 1 if $pen_width eq ''; $color = 'Black' if $color eq ''; # Build the header @line = ("$prefix", " ", " ", " ", " "); # Add the points push(@line, " ", " ", " ", "> # end of PolyLine"); # Return result return join("\n$prefix", @line); } # # >>_Description:: # {{Y:_MifRectangle}} creates a MIF rectangle object. # {{radius}} is the radius of the corner (0=square corner). # sub _MifRectangle { local($left, $top, $width, $height, $radius, $prefix, $pen, $pen_width) = @_; # local($result); local(@rect); # Build the header @rect = ("$prefix", " "); # Add the shape push(@rect, " ", " ", "> # end of RoundRect"); # Return result return join("\n$prefix", @rect); } # # >>_Description:: # {{Y:_MifAddPageBackground}} adds background objects to a page. # {{master}} is the name of the master page to get the # objects from. The master page is assumed to be in a file called # {{background}}.{{mif}}. # If {{required}} is true, then a warning is output if the # master page is not found. # sub _MifAddPageBackground { local(*page, $background, $master, $required) = @_; # local(); local($bg_ext, $bg_short, $bg_file); local($_, $in_object, $in_file); # Find the file #$bg_ext = 'bg' . substr($sdf_fmext, -1); $bg_ext = 'mif'; $bg_short = &NameJoin('', $background, $bg_ext); $bg_file = &SDF_USER'FindFile($bg_short); if ($bg_file eq '') { &AppMsg("warning", "unable to find background file '$bg_short'"); return; } # Open the file unless (open(BGFILE, $bg_file)) { &AppMsg("warning", "unable to open background file '$bg_file'"); return; } # Copy the objects $in_object = 0; $in_file = 0; while () { chop; if ($in_object) { push(@page, $_); $in_object = 0 if /^ \>/; } elsif ($in_file) { last if /^\>/; next if /\>$/; next if /^ \/) { $in_file = 1; } } close(BGFILE); # Output a warning, if necessary if ($required && !$in_file) { &AppMsg("warning", "master page '$master' not found in '$bg_file'"); } } # # >>_Description:: # {{Y:_MifAddRefPages}} adds the reference pages to the internal buffers. # {{part_type}} is the book part type. If {{part_type}} is a derived # list (e.g. TOC), the relevant special text flow is added. # sub _MifAddRefPages { local($part_type) = @_; # local(); local(@page); local($name); local(%attr); local($page_x, $page_y, $width, $height); local($objects); local($pen, $pen_width); local($y, $length); # Build the reference page header @page = ( "", " ", " "); # Add the frames $page_x = 72; $page_y = 72; for $name (sort keys %_mif_frames) { %attr = &_MifAttrSplit($_mif_frames{$name}); $width = $attr{'Width'}; $width = $SDF_USER'var{'DOC_FULL_WIDTH'} if $width eq ''; $height = $attr{'Height'}; $page_y += $height + 36; # Build the object in the frame, if necessary if ($attr{'LineLength'}) { $pen = $attr{'Pen'}; $pen_width = $attr{'PenWidth'}; $x = $attr{'LineX'}; $x = 0 if $x eq ''; $y = $attr{'LineY'}; $y = 0 if $y eq ''; $length = $attr{'LineLength'}; $objects = &_MifLine($x, $y, $length+$x, $y, " ", $pen, $pen_width, $attr{'Color'}); } else { $objects = $attr{'Objects'}; } # Add the frame push(@page, " ", " ", " ", " ", " ", " ", " ", $objects, " > # end of Frame"); } # Add the object footer push(@page, "> # end of Page"); # Add the page to the internal buffers push(@_mif_pages, join("\n", @page)); # Add the special text flow, if necessary &_MifAddSpecialTextFlow($part_type); } sub _MifAddSpecialTextFlow { local($name) = @_; # local(); local(%attr, $layout); local(@mif_text, $j, $hdgtag, $tag, $tagtype, $tagbase); local($id); local(@page); local($left, $top, $width, $height, $sh_width, $sh_gap); # Get the attributes, if any if ($_mif_lists{$part_type}) { %attr = &_MifAttrSplit($_mif_lists{$name}); } elsif ($_mif_indexes{$part_type}) { %attr = &_MifAttrSplit($_mif_indexes{$name}); } else { return; } # Build the MIF for the text flow. # Note: Frame core dumps(!!) if a layout includes a paranumonly # or paranum when the matching paragraph has no autonumber, so # we make sure this cannot happen!!! @mif_text = (); if ($name eq 'TOC') { for ($j = 1; $j <= $SDF_USER'var{'DOC_TOC'}; $j++) { for $tagtype ('H', 'A', 'P') { $hdgtag = $SDF_USER'parastyles_to{"$tagtype$j"}; $tag = $hdgtag . $name; $_mif_parastyle_used{$tag}++; $layout = &_MifFixLayout($attr{'Layout'}, $hdgtag); push(@mif_text, "", " ", " >", "> # end of Para"); } } # Ensure the index makes it into the contents $hdgtag = $SDF_USER'parastyles_to{"IXT"}; $tag = $hdgtag . $name; $_mif_parastyle_used{$tag}++; $layout = &_MifFixLayout($attr{'Layout'}, $hdgtag); push(@mif_text, "", " ", " >", "> # end of Para"); } elsif ($name eq 'LOF') { $hdgtag = $SDF_USER'parastyles_to{"FT"}; $tag = $hdgtag . $name; $_mif_parastyle_used{$tag}++; $layout = &_MifFixLayout($attr{'Layout'}, $hdgtag); push(@mif_text, "", " ", " >", "> # end of Para"); } elsif ($name eq 'LOT') { $hdgtag = $SDF_USER'parastyles_to{"TT"}; $tag = $hdgtag . $name; $_mif_parastyle_used{$tag}++; $layout = &_MifFixLayout($attr{'Layout'}, $hdgtag); push(@mif_text, "", " ", " >", "> # end of Para"); } elsif ($name eq 'IX') { for $tagbase ('GroupTitles', 'Index', 'Level1', 'Level2') { $tag = $tagbase . $name; $_mif_parastyle_used{$tag}++ if $_mif_paras{$tag}; } } # Add the text flow to the internal buffers $id = &_MifAddTextFlow(*mif_text, $name, 1); # Build the reference page header @page = ( "", " ", " "); # Calculate the page dimensions $left = &SdfVarPoints("OPT_MARGIN_INNER"); $width = &SdfVarPoints("DOC_FULL_WIDTH"); $top = &SdfVarPoints("OPT_MARGIN_TOP"); $height = &SdfVarPoints("DOC_TEXT_HEIGHT"); $sh_width = &SdfVarPoints("OPT_SIDEHEAD_WIDTH"); $sh_gap = &SdfVarPoints("OPT_SIDEHEAD_GAP"); # Add the text area &_MifAddTextArea(*page, $id, $left, $top, $width, $height, $sh_width, $sh_gap); # Add the object footer push(@page, "> # end of Page"); # Add the page to the internal buffers push(@_mif_pages, join("\n", @page)); } # # >>_Description:: # {{Y:_MifFixLayout}} removes paranum/paranumonly building blocks # from a layout for a paragraph which does not have an autonumber. # sub _MifFixLayout { local($layout, $paratag) = @_; local($result); local(%attr); # Build the result $result = $layout; %attr = &_MifAttrSplit($_mif_paras{$paratag}); if ($attr{'NumFormat'} eq '') { $result =~ s/\<\$paranumonly\\\>//g; $result =~ s/\<\$paranum\\\>//g; } #print STDERR "$paratag:$result.\n"; # Return result return $result; } # # >>_Description:: # {{Y:_MifAddLists}} adds the generated lists (i.e. table of contents, etc.). # sub _MifAddLists { local(*text) = @_; # local(); local($target, $soft); local($name); local(%attr); local($layout); local($i); local($old_match_rule, $toc_offset); # Set some flags based on the output ultimately generated $target = $SDF_USER'var{'OPT_TARGET'}; $soft = $target eq 'help' || $target eq 'html'; # Process the list definitions (add xrefs, etc.) for $name (sort keys %_mif_lists) { %attr = &_MifAttrSplit($_mif_lists{$name}); $layout = $attr{'Layout'}; $layout = '<$paratext\>' if $soft; $_mif_xrefs{$name} = $layout; } # Add titles if (@_mif_toc_list) { unshift(@_mif_toc_list, &_MifListTitle('TOC', 'Table of Contents')); } if (@_mif_lof_list) { unshift(@_mif_lof_list, &_MifListTitle('LOF', 'List of Figures')); } if (@_mif_lot_list) { unshift(@_mif_lot_list, &_MifListTitle('LOT', 'List of Tables')); } # Insert the generated lists before the first level 1 heading push(@_mif_toc_list, @_mif_lof_list, @_mif_lot_list); if (@_mif_toc_list) { $old_match_rule = $*; $* = 1; $toc_offset = 0; para: for ($i = 0; $i <= $#text; $i++) { if ($text[$i] =~ /\/) { $toc_offset = $i; last para; } } $* = $old_match_rule; splice(@text, $toc_offset, 0, @_mif_toc_list); } } # # >>_Description:: # {{Y:_MifListTitle}} builds a title for a generated list. # The mif for the title is returned. {{default_title}} is used if # a title hasn't been specified via the appropriate SDF variable. # sub _MifListTitle { local($type, $default_title) = @_; local($mif); local($title, $tag); local(@sdf_data, @mif_data); # If we're building component covers, make sure that the relevant SDF # macro gets called $mif = ''; if ($SDF_USER'var{'OPT_COMPONENT_COVER'} && ($type eq 'TOC' || $type eq 'IX')) { @sdf_data = ('!DOC_COMPONENT_COVER_BEGIN'); &_MifAddSection(*mif_data, *sdf_data); $mif = join("\n", @mif_data) . "\n"; } $tag = $SDF_USER'parastyles_to{$type . 'T'}; $_mif_parastyle_used{$tag}++; $title = $SDF_USER'var{"DOC_${type}_TITLE"}; $title = $default_title if $title eq ''; &_MifEscape(*title); $mif .= "\n" . " \n" . " >\n" . "> # end of Para"; # Return result return $mif; } # # >>_Description:: # {{Y:_MifMerge}} merges an import table into a MIF template. # The import table must be generated so that each # main MIF object contains one record only. {{offset}} contains # the indices of each main MIF object which exists in {{@import}}. # Reference pages and their textflows are retained from {{@template}}. # The other pages (i.e. the master and body pages) must be supplied # by {{@import}}. # sub _MifMerge { local(*template, *import, %offset) = @_; local(@new); local($record, $obj); local($old_match_rule); local($merged_pages, $merged_textflows, %ref_textflow); local($side_width); local($page_type, $page_name, $page_size, $cover_rect); # To permit multi-line matching, save the old state here and # restore it later $old_match_rule = $*; $* = 1; # # Do the merge. We ignore BookComponent objects # in @import as this simplifies the code somewhat. # As a Tbls section may or may not exist in the template, # we ignore existing Tbls and place new tables, if any, # immediately after figures (AFrames). # # We use while/shift, rather than for, to save memory. # $merged_pages = 0; $merged_textflows = 0; %ref_textflow = (); while($record = shift(@template)) { # Find the object 'name' unless ($record =~ /^\<(\w+)/) { &AppExit("fatal", "MIF template error - expecting object"); } $obj = $1; # Patch the comment to claim responsibility if ($obj eq 'MIFFile') { $record =~ s/\>.*$/>/; $record .= " # Generated by $app_product_name $app_product_version"; } # Patch in records from import if ($obj eq 'TextFlow') { # If this is the first text flow, merge the import text flows unless ($merged_textflows) { $merged_textflows = 1; push(@new, $import[$offset{'TextFlow'}]); } # If this is a text flow for a reference page, keep it if ($record =~ /\/) { $ref_textflow{$1} = 1; } #else { # &AppMsg("warning", "MIF template error - no TextRect ID for reference page"); #} } } elsif ($obj eq 'AFrames') { if ($offset{$obj}) { push(@new, $import[$offset{$obj}]); } if ($offset{"Tbls"}) { push(@new, $import[$offset{"Tbls"}]); } } elsif ($obj ne 'BookComponent' && $obj ne 'Tbls' && $offset{$obj}) { push(@new, $import[$offset{$obj}]); } else { push(@new, $record); } } # Return result $* = $old_match_rule; return @new; } # # >>_Description:: # {{Y:_MifHandlerTuning}} handles the 'tuning' directive. # sub _MifHandlerTuning { local(*outbuffer, $tuning, %attr) = @_; # local(); local($template_file); # Regardless of what happens, make sure we init things &_MifInitTemplate(); # A tuning of '.' means to skip merging (used in testing) if ($tuning eq '.') { return; } # Find the template file. A template file is searched for by # looking for {{tuning}}.{{fmver}} along the SDF include # path, where {{sdfver}} is fm4 or fm5 (typically). $template_file = &SDF_USER'FindFile(&NameJoin('', $tuning, $sdf_fmext)); if ($template_file eq '') { #&AppMsg("warning", "unable to find template '$tuning'"); return; } # Load the template file unless (&_MifFetchTemplate($template_file)) { &AppMsg("warning", "unable to load template file '$template_file'"); } } # # >>_Description:: # {{Y:_MifHandlerEndTuning}} handles the 'endtuning' directive. # sub _MifHandlerEndTuning { local(*outbuffer, $text, %attr) = @_; # local(); # do nothing } # # >>_Description:: # {{Y:_MifHandlerTable}} handles the 'table' directive. # sub _MifHandlerTable { local(*outbuffer, $columns, %attr) = @_; # local(); local($tbl_id); local($style, $style_tag); local($tbl_title); local($tbl_width, $unit_width, $width, @widths); local($rest); local($target); local(@equal_cols, $col); local($lower, $sep, $upper); local($indent); local($align, $placement); local($margins); # Update the state push(@_mif_tbl_state, $_MIF_INTABLE); push(@_mif_tbl_start, $#outbuffer + 1); push(@_mif_tbl_wide, $attr{'wide'}); push(@_mif_tbl_style, $attr{'style'}); push(@_mif_tbl_landscape, $attr{'landscape'}); push(@_mif_row_type, ''); # Get the table id. Note that we keep a queue of these (rather # than a stack) to ensure that nested table are output in the # right order. $tbl_id = $#_mif_table + scalar(@_mif_tbl_id) + $_MIF_OBJ_REF_START; unshift(@_mif_tbl_id, $tbl_id); # Get the style $style = $attr{'style'}; $style_tag = $SDF_USER'tablestyles_to{$style}; if ($style_tag eq '') { &AppMsg("warning", "unknown table style '$style'"); $style_tag = $style; } # Get the title, if any. FrameMaker 5.0 adds blank pages for # tables with titles, so a configuration flag controls whether # we produce real or simulated table titles. $tbl_title = $attr{'title'}; push(@_mif_tbl_title, $tbl_title); $tbl_title = '' if $_MIF_SIMPLE_TBL_TITLES; # Get the indent if (!$attr{'wide'} && $attr{'listitem'} ne '') { $indent = &SdfVarPoints('OPT_LIST_INDENT') * $attr{'listitem'}; } else { $indent = 0; } # Get the positioning details $align = $attr{'align'}; $align = 'Inside' if $align eq 'Inner'; $align = 'Outside' if $align eq 'Outer'; $placement = $attr{'placement'}; $placement =~ s/top/Top/ if $placement ne ''; # For landscape tables, the default alignment is centered, i.e. # the table is centered BEFORE rotation if ($attr{'landscape'} && $align eq '') { $align = 'Center'; } # Get the table width and 1% (i.e. unit) width if ($attr{'wide'}) { $tbl_width = $SDF_USER'var{'DOC_FULL_WIDTH'} - $indent; } else { $tbl_width = $SDF_USER'var{'DOC_TEXT_WIDTH'} - $indent; } $unit_width = $tbl_width / 100; # For margins not supplied, we use some defaults. Ideally, # the default values should come from the table format used. ($_mif_tbl_lmargin, $_mif_tbl_tmargin, $_mif_tbl_rmargin, $_mif_tbl_bmargin) = (6, 4, 6, 2); # Get the cell margins, if necessary if ($attr{'lmargin'} ne '' || $attr{'tmargin'} ne '' || $attr{'rmargin'} ne '' || $attr{'bmargin'} ne '') { $_mif_tbl_lmargin = $attr{'lmargin'} if defined $attr{'lmargin'}; $_mif_tbl_tmargin = $attr{'tmargin'} if defined $attr{'tmargin'}; $_mif_tbl_rmargin = $attr{'rmargin'} if defined $attr{'rmargin'}; $_mif_tbl_bmargin = $attr{'bmargin'} if defined $attr{'bmargin'}; $margins = "${_mif_tbl_lmargin}pt ${_mif_tbl_tmargin}pt " . "${_mif_tbl_rmargin}pt ${_mif_tbl_bmargin}pt"; } # Update the output buffer $_mif_tblstyle_used{$style_tag}++; push(@outbuffer, " ", " "); # If we're ultimately going to rtf or hlp, output a simple set of # column widths $target = $SDF_USER'var{'OPT_TARGET'}; if ($target eq 'hlp' || $target eq 'rtf') { push(@outbuffer, " "); @widths = split(/,/, $attr{'format'}); $rest = 100; for ($col = 0; $col < $columns; $col++) { $width = $widths[$col]; if ($width =~ /([-=])/) { # Assume 30% for now :-( $width = (30 * $unit_width) . 'pt'; $rest -= 30; } elsif ($width =~ /\*$/) { # Assume the rest for now. This will work provided # that it is last column and that preceding dimensions are # guessed (e.g. -) or percentages. :-( $rest = 10 if $rest <= 0; $width = ($rest * $unit_width) . 'pt'; $rest = 0; } else { # Convert the measurement to points if it's a percentage if ($width =~ /\%$/) { $rest -= $`; $width = ($` * $unit_width) . 'pt'; } } push(@outbuffer, " "); } } else { # Override the table format push(@outbuffer, " ") if $tbl_title; push(@outbuffer, " ") if $indent ne ''; push(@outbuffer, " ") if $align; push(@outbuffer, " ") if $placement; push(@outbuffer, " ") if $margins; push(@outbuffer, " "); # Output the column widths @widths = split(/,/, $attr{'format'}); @equal_cols = (); for ($col = 0; $col < $columns; $col++) { $width = $widths[$col]; push(@outbuffer, " "); if ($width =~ /([-=])/) { $lower = $`; $sep = $1; $upper = $'; $lower = ($` * $unit_width) . 'pt' if $lower =~ /\%$/; $upper = ($` * $unit_width) . 'pt' if $upper =~ /\%$/; push(@outbuffer, " "); push(@equal_cols, $col) if $sep eq '='; } elsif ($width =~ /\*$/) { push(@outbuffer, " "); } else { $width = ($` * $unit_width) . 'pt' if $width =~ /\%$/; push(@outbuffer, " "); } push(@outbuffer, " > # end of TblColumn"); } push(@outbuffer, " > # end of TblFormat"); # Output the column details (those not already in the table format) push(@outbuffer, " "); if (@equal_cols) { push(@outbuffer, " "); } push(@outbuffer, " > # end of EqualizeWidths"); } } # Output the table title, if any if ($tbl_title) { push(@outbuffer, " # end of TblTitleContent"); } } # # >>_Description:: # {{Y:_MifHandlerRow}} handles the 'row' directive. # sub _MifHandlerRow { local(*outbuffer, $text, %attr) = @_; # local(); local($state); # Finalise the old cell/row, if any $state = $_mif_tbl_state[$#_mif_tbl_state]; if ($state eq $_MIF_INCELL) { push(@outbuffer, " > # end of CellContent", " > # end of Cell", " > # end of Row", " > # end of TblH/TblBody/TblF"); } elsif ($state eq $_MIF_INROW) { push(@outbuffer, " > # end of Row", " > # end of TblH/TblBody/TblF"); } # Update the state $_mif_tbl_state[$#_mif_tbl_state] = $_MIF_INROW; $_mif_row_type[$#_mif_row_type] = $text; # Update the output buffer push(@outbuffer, " ") if $text eq 'Group'; } # # >>_Description:: # {{Y:_MifHandlerCell}} handles the 'cell' directive. # sub _MifHandlerCell { local(*outbuffer, $text, %attr) = @_; # local(); local($state); local($fill, $color, $cols, $rows, $angle); local($lruling, $rruling, $truling, $bruling); # Finalise the old cell, if any $state = $_mif_tbl_state[$#_mif_tbl_state]; if ($state eq $_MIF_INCELL) { push(@outbuffer, " > # end of CellContent", " > # end of Cell"); } # Update the state $_mif_tbl_state[$#_mif_tbl_state] = $_MIF_INCELL; $_mif_cell_align = $attr{'align'}; $_mif_cell_valign = $attr{'valign'}; if ($_mif_cell_valign eq 'Baseline') { $_mif_cell_valign = ''; } $_mif_cell_tmargin = $attr{'tmargin'}; $_mif_cell_bmargin = $attr{'bmargin'}; $_mif_cell_lmargin = $attr{'lmargin'}; $_mif_cell_rmargin = $attr{'rmargin'}; # Get the attributes $color = &_MifMapColor($attr{'bgcolor'}); $fill = $attr{'fill'} ne '' ? $_MIF_FILL_CODE{$attr{'fill'}} : ($color ne '' ? 0 : ''); $cols = $attr{'cols'}; $rows = $attr{'rows'}; $angle = $attr{'angle'}; $lruling = $attr{'lruling'}; $lruling = 'Very Thin' if $lruling eq 'Vthin'; $rruling = $attr{'rruling'}; $rruling = 'Very Thin' if $rruling eq 'Vthin'; $truling = $attr{'truling'}; $truling = 'Very Thin' if $truling eq 'Vthin'; $bruling = $attr{'bruling'}; $bruling = 'Very Thin' if $bruling eq 'Vthin'; # For tables of style "columns", the default top ruling of # group rows is 'Thin' if ($_mif_row_type[$#_mif_row_type] eq 'Group' && $truling eq '') { $truling = 'Thin' if $_mif_tbl_style[$#_mif_tbl_style] eq 'columns'; } # Update the output buffer push(@outbuffer, " ") if $fill ne ''; push(@outbuffer, " ") if $color ne ''; push(@outbuffer, " ") if $cols > 1; push(@outbuffer, " ") if $rows > 1; push(@outbuffer, " ") if $angle != 0; push(@outbuffer, " ") if $lruling ne ''; push(@outbuffer, " ") if $rruling ne ''; push(@outbuffer, " ") if $truling ne ''; push(@outbuffer, " ") if $bruling ne ''; push(@outbuffer, " >_Description:: # {{Y:_MifHandlerEndTable}} handles the 'endtable' directive. # sub _MifHandlerEndTable { local(*outbuffer, $text, %attr) = @_; # local(); local($state, $start, $tbl_id, $tbl_title, $tbl_wide); local($nested, $angle); # Finalise the table $state = pop(@_mif_tbl_state); if ($state eq $_MIF_INCELL) { push(@outbuffer, " > # end of CellContent", " > # end of Cell", " > # end of Row", " > # end of TblH/TblBody/TblF"); } elsif ($state eq $_MIF_INROW) { push(@outbuffer, " > # end of Row", " > # end of TblH/TblBody/TblF"); } push(@outbuffer, " > # end of Tbl"); # Move the table to the table buffer $start = pop(@_mif_tbl_start); push(@_mif_table, join("\n", @outbuffer[$start .. $#outbuffer])); $#outbuffer = $start - 1; # Update the main text flow $tbl_id = pop(@_mif_tbl_id); $tbl_wide = pop(@_mif_tbl_wide); $tbl_title = pop(@_mif_tbl_title); $landscape = pop(@_mif_tbl_landscape); if (@_mif_tbl_id) { &'AppMsg("warning", "ignoring nested MIF table"); } else { if ($landscape) { $nested = $landscape; $angle = 90; } &_MifUpdateMainFlow(*outbuffer, "table", $tbl_id, $tbl_title, $tbl_wide, $nested, $angle); } # Cleanup the state pop(@_mif_tbl_style); pop(@_mif_row_type); } # # >>_Description:: # {{Y:_MifHandlerInline}} handles the inline directive. # sub _MifHandlerInline { local(*outbuffer, $text, %attr) = @_; # local(); # Check we can handle this format my $target = $attr{'target'}; return unless $target eq 'mif' || $target eq 'ps'; # Build the result push(@outbuffer, $text); } # # >>_Description:: # {{Y:_MifHandlerOutput}} handles the output directive. # sub _MifHandlerOutput { local(*outbuffer, $text, %attr) = @_; # local(); local($offset, @component_data); local($component_type); local($file); # Finalise the current component, if requested if ($text eq '-') { # If there is no current component, do nothing return unless @_mif_component_offset; # Find the component type $component_type = $_mif_component_type[$_mif_component_cursor++]; # Generate the mif $offset = pop(@_mif_component_offset); @component_data = splice(@outbuffer, $offset + 1); @component_data = &_MifFinalise(*component_data, $component_type); # Output the mif $file = pop(@_mif_component_file); unless (open(CHAPTER, ">$file")) { &AppMsg("error", "unable to write to component file '$file'"); return; } print CHAPTER join("\n", @component_data), "\n"; close(CHAPTER); } # Otherwise, save the output filename and the current offset # Note: the type is pushed onto @_mif_component_type by MifNewComponent. else { push(@_mif_component_file, $text); push(@_mif_component_offset, $#outbuffer); } } # # >>_Description:: # {{Y:_MifHandlerImport}} handles the import directive. # sub _MifHandlerImport { local(*outbuffer, $filepath, %attr) = @_; # local(); local($ref_id); # Add the figure to the internal buffers $ref_id = &_MifAddFigure($filepath, *attr); # Reference this object in the main flow &_MifUpdateMainFlow(*outbuffer, 'figure', $ref_id, $attr{'title'}, $attr{'wide'}); } # # >>_Description:: # {{Y:_MifAddFigure}} adds a figure to the internal buffers. # The reference id of the figure is returned. # sub _MifAddFigure { local($filepath, *attr) = @_; local($ref_id); local($fullname); local($ext); # Get the complete pathname and the file extension $fullname = $attr{'fullname'}; $ext = (&NameSplit($fullname))[2]; # Get the reference in the output buffer if ($ext eq 'mif') { $ref_id = &_MifAdd($fullname, 'figure', 1, *attr); } elsif ($attr{'mif_figure'}) { $ref_id = &_MifAdd($fullname, 'figure', $attr{'mif_figure'}, *attr); } else { $attr{'position'} = 'RunIntoParagraph' if $attr{'wrap_text'}; $attr{'position'} = 'below' unless $attr{'position'}; $ref_id = &_MifAddRef($filepath, %attr); } # Return result return $ref_id; } # # >>_Description:: # {{Y:_MifHandlerObject}} handles the 'object' directive. # sub _MifHandlerObject { local(*outbuffer, $type, %attrs) = @_; # local(); local($fn); local($name, $parent); # Find the object handler, if any $fn = "_MifObjectHandler$type"; if (defined &$fn) { # Get the name and parent $name = $attrs{'Name'}; delete $attrs{'Name'}; $parent = $attrs{'Parent'}; delete $attrs{'Parent'}; # Jump to the routine which handles this object &$fn(*outbuffer, $name, $parent, %attrs); } } # # >>_Description:: # {{Y:_MifAddToCatalog}} adds an object to a catalog of objects. # If {{$parent}} and {{$root}} are set and equal, then {{%root_attr}} is # used instead of retrieving the parent attributes from the catalog. # This is done to improve performance. # sub _MifAddToCatalog { local(*catalog, $type, $name, $parent, *attr, *root, *root_attr) = @_; # local(); local(%parent_attr); # Get the parent definition, if any %parent_attr = (); if ($parent ne '') { if ($parent eq $root) { %parent_attr = %root_attr; } else { %parent_attr = &_MifAttrSplit($catalog{$parent}); } unless (%parent_attr) { &AppMsg("warning", "unknown parent '$parent' in definition of mif $type '$name'"); return; } } # Merge in the new attributes @parent_attr{keys %attr} = values %attr; # Store the new definition $catalog{$name} = &_MifAttrJoin(*parent_attr); # Save the root, if necessary if ($root eq '' && $parent eq '') { $root = $name; %root_attr = %attr; } } # # >>_Description:: # {{Y:_MifObjectHandlerVariable}} defines 'Variable' objects. # sub _MifObjectHandlerVariable { local(*outbuffer, $name, $parent, %attr) = @_; # local(); local($value); $name = $_MIF_VAR_MAP{$name} if $_MIF_VAR_MAP{$name}; $value = $attr{'value'}; if ($name =~ /^MIF_/) { return if $name eq 'MIF_TABLE_UNIT'; return if $name eq 'MIF_TABLE_UNIT_WIDE'; if ($name eq 'MIF_COVER') { $_mif_cover = $value; } else { $name = 'D' . &MiscUpperToMixed($'); $_mif_ctrls{$name} = $value; } } else { $_mif_vars{$name} = $value; } } # # >>_Description:: # {{Y:_MifObjectHandlerXref}} defines 'Xref' objects. # sub _MifObjectHandlerXref { local(*outbuffer, $name, $parent, %attr) = @_; # local(); $_mif_xrefs{$name} = $attr{'value'}; } # # >>_Description:: # {{Y:_MifObjectHandlerPara}} defines 'Para' objects, # i.e. it generates a new paragraph format in the output. # sub _MifObjectHandlerPara { local(*outbuffer, $name, $parent, %attr) = @_; # local(); &_MifAddToCatalog(*_mif_paras, 'paragraph', $name, $parent, *attr, *_mif_pararoot_name, *_mif_pararoot_attr); } # # >>_Description:: # {{Y:_MifObjectHandlerPhrase}} defines 'Phrase' objects, # i.e. it generates a new font format in the output. # sub _MifObjectHandlerPhrase { local(*outbuffer, $name, $parent, %attr) = @_; # local(); &_MifAddToCatalog(*_mif_fonts, 'font', $name, $parent, *attr, *_mif_fontroot_name, *_mif_fontroot_attr); } # # >>_Description:: # {{Y:_MifObjectHandlerTable}} defines 'Table' objects, # i.e. it generates a new table format in the output. # {{tags}} contains the new and base tags separated by a space. # sub _MifObjectHandlerTable { local(*outbuffer, $name, $parent, %attr) = @_; # local(); &_MifAddToCatalog(*_mif_tbls, 'table', $name, $parent, *attr, *_mif_tblroot_name, *_mif_tblroot_attr); } # # >>_Description:: # {{Y:_MifObjectHandlerFrame}} defines 'Frame' objects, # i.e. it generates a reference frame in the output. # sub _MifObjectHandlerFrame { local(*outbuffer, $name, $parent, %attr) = @_; # local(); &_MifAddToCatalog(*_mif_frames, 'frame', $name, $parent, *attr, *_mif_frameroot_name, *_mif_frameroot_attr); } # # >>_Description:: # {{Y:_MifObjectHandlerList}} defines 'List' objects. # i.e. it defines the layout of the TOC (Table of Contents), etc. # sub _MifObjectHandlerList { local(*outbuffer, $name, $parent, %attr) = @_; # local(); &_MifAddToCatalog(*_mif_lists, 'lists', $name, $parent, *attr, *_mif_listroot_name, *_mif_listroot_attr); } # # >>_Description:: # {{Y:_MifObjectHandlerIndex}} defines 'Index' objects. # i.e. it defines the layout of an index # sub _MifObjectHandlerIndex { local(*outbuffer, $name, $parent, %attr) = @_; # local(); &_MifAddToCatalog(*_mif_indexes, 'indexes', $name, $parent, *attr, *_mif_indexroot_name, *_mif_indexroot_attr); } # # >>_Description:: # {{Y:_MifPhraseHandlerChar}} handles the 'char' phrase directive. # sub _MifPhraseHandlerChar { local(*para, $text, %attr) = @_; # local(); local($char); local($hex); $char = $_MIF_CHAR{$text}; if ($text =~ /^\d+/) { $hex = sprintf("%x", $text); $para .= " \n"; } elsif ($char =~ /^\W/) { $para .= " \n"; } elsif ($char) { $para .= " \n"; } else { &AppMsg("warning", "ignoring unknown character '$text'"); } # Hard returns must be the last thing in a ParaLine if ($char eq 'HardReturn' || $text == $_MIF_HARDRETURN_CODE) { $para .= " > # end of ParaLine\n" . " >_Description:: # {{Y:_MifPhraseHandlerInline}} handles the 'inline' phrase directive. # sub _MifPhraseHandlerInline { local(*para, $text, %attr) = @_; # local(); # Build the result $para .= $text; } # # >>_Description:: # {{Y:_MifPhraseHandlerImport}} handles the 'import' phrase directive. # sub _MifPhraseHandlerImport { local(*para, $filepath, %attr) = @_; # local(); # Add the figure to the internal buffers $attr{'position'} = 'inline' unless $attr{'position'}; $ref_id = &_MifAddFigure($filepath, *attr); # Add the reference to the paragraph $para .= " \n"; } # # >>_Description:: # {{Y:_MifPhraseHandlerVariable}} handles the 'variable' phrase directive. # sub _MifPhraseHandlerVariable { local(*para, $text, %attr) = @_; # local(); $para .= " \n" . " > # end of Variable\n"; } # # >>_Description:: # {{Y:_MifPhraseHandlerXRef}} handles the 'xref' phrase directive. # sub _MifPhraseHandlerXRef { local(*para, $para_text, %attr) = @_; # local(); local($format, $text, $file); # Get the text and file from the jump attribute. $format = $attr{'xref'}; ($file, $text) = split(/#/, $attr{'jump'}, 2); # Assume the Frame file, if any, has a doc extension. if ($file ne '') { $file = &NameSubExt($file, 'doc'); } # Build the cross-reference $para .= " \n" . " \n" . " \n" . " > # end of XRef\n"; } # # >>_Description:: # {{Y:_MifPhraseHandlerPageNum}} handles the 'pagenum' phrase directive. # sub _MifPhraseHandlerPageNum { local(*para, $para_text, %attr) = @_; # local(); &_MifPhraseHandlerVariable(*para, 'Current Page #'); } # # >>_Description:: # {{Y:_MifPhraseHandlerPageCount}} handles the 'pagecount' phrase directive. # sub _MifPhraseHandlerPageCount { local(*para, $para_text, %attr) = @_; # local(); &_MifPhraseHandlerVariable(*para, 'Page Count'); } # # >>_Description:: # {{Y:_MifPhraseHandlerParaText}} handles the 'paratext' phrase directive. # sub _MifPhraseHandlerParaText { local(*para, $tags, %attr) = @_; # local(); &_MifRunningHF(*para, 'paratext', $tags); } # # >>_Description:: # {{Y:_MifPhraseHandlerParaNum}} handles the 'paranum' phrase directive. # sub _MifPhraseHandlerParaNum { local(*para, $tags, %attr) = @_; # local(); &_MifRunningHF(*para, 'paranum', $tags); } # # >>_Description:: # {{Y:_MifPhraseHandlerParaNumOnly}} handles the 'paranumonly' phrase directive. # sub _MifPhraseHandlerParaNumOnly { local(*para, $tags, %attr) = @_; # local(); &_MifRunningHF(*para, 'paranumonly', $tags); } # # >>_Description:: # {{Y:_MifPhraseHandlerParaShort}} handles the 'parashort' phrase directive. # sub _MifPhraseHandlerParaShort { local(*para, $tags, %attr) = @_; # local(); &_MifRunningHF(*para, 'marker1'); } # # >>_Description:: # {{Y:_MifPhraseHandlerParaLast}} handles the 'paralast' phrase directive. # sub _MifPhraseHandlerParaLast { local(*para, $tags, %attr) = @_; # local(); &_MifRunningHF(*para, 'paratext', "+,$tags"); } # # >>_Description:: # {{Y:_MifRunningHF}} defines and inserts a Running H/F variable. # {{type}} is one of paratext, paranum, paranumonly, # marker1 or marker2. For the first 3 of these, {{tags}} is a # comma separated list of paragraph tags. # sub _MifRunningHF { local(*para, $type, $tags) = @_; # local(); local($defn); local(@tags, $tag, $fmtag); local($num); local($varname); # Build the definition if ($tags eq '') { $defn = "<\$$type>"; } else { # Convert the SDF tag names to Frame ones @tags = split(/,/, $tags); for $tag (@tags) { $fmtag = $SDF_USER'parastyles_to{$tag}; $tag = $fmtag if $fmtag ne ''; } $defn = "<\$$type\[" . join(",", @tags) . "]>"; } # If the definition matches an existing running h/f variable, reuse it for ($num = 1; $num <= $_mif_runninghf_cnt; $num++) { last if $_mif_vars{"Running H/F $num"} eq $defn; } # If necessary, update the running h/f counter, # checking we haven't used them all if ($num > 4) { &AppMsg("warning", "ignoring MIF directive '$type' - Running H/F 4 already used"); return; } elsif ($num > $_mif_runninghf_cnt) { $_mif_runninghf_cnt++; } # Insert the variable $varname = "Running H/F $num"; $_mif_vars{$varname} = $defn; &_MifPhraseHandlerVariable(*para, $varname); } # # >>_Description:: # {{Y:_MifParaAppend}} merges {{para}} into the last paragraph # in {{@result}}. # (This is used to workaround Frame putting blank lines between # each example paragraph when it converts documents to text.) # sub _MifParaAppend { local(*result, $para) = @_; # local(); local($body_start); # find the location of the first ParaLine in the paragraph $body_start = index($para, " = 0) { substr($result[$#result], - $_MIF_PARA_END_LEN) = " \n >\n" . substr($para, $body_start) . $_MIF_PARA_END; } else { &AppMsg("failed", "bad paragraph format in '_MifParaAppend'"); } } # # >>_Description:: # {{Y:_MifAdd}} adds an object to the internal lists. These # lists are merged with the converted text in {{Y:_MifFinalise}} # to produce the final MIF file. {{id}} can be either: # # * a number (the internal object number in the file), or # * a string (a pattern matched in the text just before the object # is referenced in the main text flow of the file) # sub _MifAdd { local($file, $what, $id, *attr) = @_; local($ref_id); local($ok); local(@file); local($obj_id, $tag, $tbl_begin, $fmt_id, $fmt_ok); # Fetch the file ($ok, @file) = &_MifFetchAlbum($file); unless ($ok) { &AppMsg("warning", "failed to fetch file '$file'"); return 0; } # If a pattern is passed as the id, first convert it to an # object reference if ($id =~ /^\d+$/) { $obj_id = $id; } else { $obj_id = &MifIdLookupBySymbol(*file, $_MIF_REF{$what}, $id); } # Add the object itself & format, if any, onto our stacks if ($obj_id) { if ($what eq 'table') { ($ref_id, $tbl_begin) = &_MifAddObject(*file, 'Tbl', 'TblID', $obj_id, 'stack', *_mif_table); # Assume the table format id is the second attribute if ($ref_id) { $file[$tbl_begin + 2] =~ /TblTag (.*)\>/; $fmt_id = $1; $_mif_tblstyle_used{$fmt_id}++; ($fmt_ok) = &_MifAddObject(*file, 'TblFormat', 'TblTag', $fmt_id, 'byname', *_mif_tbls); unless ($fmt_ok) { &AppMsg("warning", "MIF table format clash ($file, $id, $fmt_id)"); } } } else { ($ref_id) = &_MifAddObject(*file, 'Frame', 'ID', $obj_id, 'stack', *_mif_figure, *attr); } } # Check object was found unless ($ref_id) { &AppMsg("warning", "mif object '$what $id' not found in file '$file'"); } # Return result return $ref_id; } # # >>_Description:: # {{Y:_MifAddRef}} adds a referenced figure to the internal lists. These # lists are merged with the converted text in {{Y:_MifFinalise}} # to produce the final MIF file. # {{%attr}} contains tuning attributes. # sub _MifAddRef { local($name, %attr) = @_; local($ref_id); local($fullname); local($width, $height); local($mif_path); local($shape_rect_params); local(@frame); # Default the height and width, if we can $fullname = $attr{'fullname'}; ($width, $height) = &SdfSizeGraphic($fullname); $attr{'width'} = $width ? $width : 20 unless $attr{'width'}; $attr{'height'} = $height ? $height : 20 unless $attr{'height'}; # points is the default measurement $width = $attr{'width'}; $height = $attr{'height'}; $width .= "pt" if $width =~ /^[\d\.]+$/; $height .= "pt" if $height =~ /^[\d\.]+$/; # Convert the name into a MIF device-independent pathname #if (! &NameIsAbsolute($name) && $attr{'root'} ne '') { #$name = &NameJoin($attr{'root'}, $name); #} if ($SDF_USER'var{'OPT_TARGET'} eq 'hlp') { $mif_path = &_MifPathName($name); } else { $mif_path = &_MifPathName($fullname); } # Build the Frame body $shape_rect_params = "0\" 0\" $width $height"; @body = ( " ", " ", " > # end of ImportObject"); # Build the frame $ref_id = scalar(@_mif_figure) + $_MIF_OBJ_REF_START; @frame = &_MifBuildFrame($ref_id, $shape_rect_params, *attr, *body); # Add the frame to the internal list push(@_mif_figure, join("\n", @frame)); # Return result return $ref_id; } # # >>_Description:: # {{_MifBuildFrame}} build a 'Frame' object, given its id, size, attributes # and body. # sub _MifBuildFrame { local($ref_id, $shape_rect_params, *attr, *body) = @_; local(@frame); local($frametype); # Map the attributes to MIF names $frametype = $attr{'position'}; $frametype = 'Below' unless $frametype; substr($frametype, 0, 1) =~ tr/a-z/A-Z/; # Build the figure frame header @frame = ( " ", " ", " "); # Add the optional stuff if ($attr{'align'}) { substr($attr{'align'}, 0, 1) =~ tr/a-z/A-Z/; push(@frame, " "); } if ($attr{'clipped'}) { push(@frame, " "); } if ($attr{'floating'}) { push(@frame, " "); } if ($attr{'bl_offset'}) { $attr{'bl_offset'} .= "pt" if $attr{'bl_offset'} =~ /^[\d\.]+$/; push(@frame, " "); } if ($attr{'ns_offset'}) { $attr{'ns_offset'} .= "pt" if $attr{'ns_offset'} =~ /^[\d\.]+$/; push(@frame, " "); } # Return result return (@frame, @body, " > # end of Frame"); } # # >>_Description:: # {{Y:_MifUpdateMainFlow}} adds a reference to the main text flow. # If {{nested}} is set, the object is nested inside a TectRect # which is allocated that percentage of the text column height and # rotated by {{angle}}. # sub _MifUpdateMainFlow { local(*outbuffer, $what, $ref_id, $title, $wide, $nested, $angle) = @_; # local(); local($tag); local(@para); # Get the paragraph tag: 'Title' or 'Body'? if ($title ne '') { $tag = $SDF_USER'parastyles_to{$_MIF_TITLE_TAG{$what}}; # Escape special characters $title =~ s/([\>\\])/\\$1/g; $title =~ s/([\t])/\\t/g; $title =~ s/(['])/\\q/g; $title =~ s/([`])/\\Q/g; } else { $tag = $SDF_USER'parastyles_to{$_MIF_NOTITLE_TAG{$what}}; } # Build the paragraph $_mif_parastyle_used{$tag}++; @para = ( "\n" . " \n") if $wide; push(@para, " \n") if $angle; push(@para, " >\n" . " \n" . " <$_MIF_REF{$what} $ref_id>\n" . " >\n" . "> # end of Para"); # Handle nested objects if ($nested ne '') { @para = &_MifAddNestedText(*para, $nested, $angle); } # Add a reference directly into the output push(@outbuffer, @para); } # # >>_Description:: # {{Y:_MifAddObject}} adds an object to the nominated internal list. # {{lookup_id}} is the object reference id (numeric). # If {{%attr}} is passed and {{how}} is stack, the object # is assumed to be a figure and the Frame header is built # from the attributes passed in. # sub _MifAddObject { local(*source, $type, $id_str, $lookup_id, $how, *where, *attr) = @_; local($new_id, $first, $last); local(@this_obj, $this_index, $i); local($shape_rect_params); local(@body); # Search for object using lookup_id record: for ($i = 0; $i < $#source; $i++) { if ($first && $source[$i] =~ /\> \# end of $type\s*$/) { $last = $i; last record; } if ($source[$i] =~ /\<$type\s*$/ && $source[$i + 1] =~ /\<$id_str $lookup_id\>/) { $first = $i; $i += 2; } } return 0 unless $last; # Save object if ($how eq 'stack') { $this_index = scalar(@where) + $_MIF_OBJ_REF_START; if (%attr) { ($shape_rect_params, @body) = &_MifRemoveFrameAttrs(@source[$first + 2 .. $last - 1]); @this_obj = &_MifBuildFrame($this_index, $shape_rect_params, *attr, *body); } else { @this_obj = (" <$type", " <$id_str $this_index>", @source[$first + 2 .. $last]); } push(@where, join("\n", @this_obj)); } elsif ($how eq 'byname') { $this_index = $lookup_id; $this_obj = join("\n", @source[$first .. $last]); if ($where{$this_index}) { if ($where{$this_index} ne $this_obj) { $this_index = ""; } } else { $where{$this_index} = $this_obj; } } # Return result return $this_index, $first, $last; } # # >>_Description:: # {{_MifRemoveFrameAttrs}} removes attributes from a Frame object. # sub _MifRemoveFrameAttrs { local(@frame) = @_; local($shape_rect_params, @body); local($junk); local($index); # Copy across the lines except those between ShapeRect and Cropped @body = (); $junk = 0; for ($index = 0; $index <= $#frame; $index++) { $line = $frame[$index]; last if $line =~ /^\s*]+)\>/) { $shape_rect_params = $1; $junk = 1; } elsif (! $junk) { push(@body, $line); } } push(@body, @frame[$index + 1 .. $#frame]); # Return result return ($shape_rect_params, @body); } # # >>_Description:: # {{Y:_MifAddNestedText}} adds paragraph text as a nested object. # {{@text}} is the text to add. The new text to add into the main text # flow is returned. {{nested}} is the percentage of the text column # to allocate to the object and {{angle}} is the angle. # sub _MifAddNestedText { local(*text, $nested, $angle) = @_; local(@result); local($textflow_id); local($width, $height, $adj_height); local($ref_id); local(@frame); local($tag); # Create the new text flow $textflow_id = &_MifAddTextFlow(*text, '', 1); # Calculate the width and height of the rotated text column. # For simplicity, the width is always the column width. # The height is calculated as a percentage of the text column # height. We also need an "adjusted height" for the embedded # text rectangle, otherwise Frame crops the table border. $width = $SDF_USER'var{'DOC_TEXT_WIDTH'}; if ($nested == 1) { $height = $SDF_USER'var{'DOC_TEXT_HEIGHT'}; } elsif ($nested =~ /^(\d+)\%?$/) { $height = $SDF_USER'var{'DOC_TEXT_HEIGHT'} * $1 / 100; } else { $height = &SdfPoints($nested); } $adj_height = $height - 2; # Build the rotated text column $ref_id = scalar(@_mif_figure) + $_MIF_OBJ_REF_START; @frame = ( " ", " ", " ", " ", " "); push(@frame, " ") if $angle != 0; push(@frame, " ", " > # end of TextRect", " > # end of Frame"); # Add it to the internal buffers push(@_mif_figure, join("\n", @frame)); # Build the new text $tag = $SDF_USER'parastyles_to{'N'}; $_mif_parastyle_used{$tag}++; @result = ( "\n" . " \n" . " >\n" . "> # end of Para"); # return result return @result; } # # >>Description:: # {{Y:MifIdLookupBySymbol}} searches in {{@source}} # for {{symbol}} closely followed by a reference of type {{ref}}. # If found, the ID of the object is returned. Otherwise, 0 is returned. # sub MifIdLookupBySymbol { local(*source, $ref, $symbol) = @_; local($id); local($safe, $i); # escape any metacharacters if symbol $safe = $symbol; $safe =~ s/(\W)/\\\1/g; # search for ($i = 0; $i < $#source; $i++) { if ($source[$i] =~ /\/ && $source[$i + 1] =~ /\<$ref\s+(\d+)\>/) { return $1; } } # If reach here, no luck return 0; } # # >>_Description:: # {{Y:_MifPathName}} converts a pathname into a MIF device-independent # pathname. # (Thanks to Prachin Ranavat for the initial code.) # sub _MifPathName { local($name) = @_; local($mif); local($k); my ($is_absolute, $os_name); my @components = NamePathComponentSplit($name); # check if path is absolute $is_absolute = &NameIsAbsolute($name); $os_name = &NameOS; SWITCH_OS: { $mif = '', last SWITCH_OS if $os_name eq 'unix' && $is_absolute; $mif = '' . shift @components, last SWITCH_OS if $os_name eq 'mac' && $is_absolute; $mif = '' . shift @components, last SWITCH_OS if $os_name eq 'dos' && $is_absolute; } foreach $k (@components) { next if !$k; if ($k eq '..' || $k eq '::') { # parent directory in path - replace by $mif .= ""; } else { # directory name in path - replace by directory_name $mif .= "".$k; } } # Return result return ($mif); } # # >>Description:: # {{Y:MifLink}} converts a hypertext link in URL format to a frame one. # {{mif_ext}} is the extension to use on Frame files in hypertext jumps. # The default value is {{fvo}}. # sub _MifLink { local($url, $mif_ext) = @_; local($mif_link); local($file, $topic); local($spec_char); # Setup special characters match string $spec_char = "\\>\\\\"; # Default the file extension to fvo, if necessary $mif_ext = 'fvo' unless $mif_ext ne ''; if ($url =~ /^([-\w\/\.]*)#/) { ($file, $topic) = ($1, $'); if ($file ne '') { $file =~ s/\.html$//; $file .= ".$mif_ext"; } $topic =~ s/([$spec_char])/\\$1/g; $mif_link = "gotolink " . ($file ? "$file:$topic" : $topic); } elsif ($url =~ /^([-\w\/\.]*)$/) { $url =~ s/\.html$//; $url .= ".$mif_ext"; $mif_link = "gotopage $url:firstpage"; } else { $mif_link = "gotourl $url"; #$mif_link = "sdf url=$url"; # once TJH fixes fm2html } # Return result return $mif_link; } # # >>_Description:: # {{Y:MifEscapeNewlink}} escapes a newlink. # (Thanks to Tim Hudson for the code.) # sub _MifEscapeNewlink { local($link) = @_; local($result); local($spec_char); ##print STDERR "NEWLINK-IN: $link\n"; # Assign default special characters match string, if necessary $spec_char = "\\>\\\\" if $spec_char eq ''; $link =~ s/([$spec_char])/\\$1/g; # escape quotes $link =~ s/\'/\\q/g; # strip backquotes $link =~ s/\`//g; 1 while $link =~ s/{{.:([^}]*)}}/$1/e; $link =~ s/^\s+//; ##print STDERR "NEWLINK-OUT: $link\n"; return $link; } # # >>Description:: # {{Y:MifNewComponent}} is an event processing routine for # paragraphs which begin a new component. # sub MifNewComponent { local($type) = @_; local($cname); # Save away the component details (so a book can be constructed later) $cname = &_MifComponentName($SDF_USER'var{'DOC_BASE'}); $type = 'chapter' if $type eq '1'; push(@_mif_component_tbl, "$cname|$type"); push(@_mif_component_type, "\U$type"); # Ensure that each component gets placed into its own output file. # (stdlib/mif.sdt takes care of the first part, so ignore it) if (scalar(@_mif_component_tbl) > 2) { &SDF_USER'PrependText( "!DOC_COMPONENT_END", "!output '-'", "!output '$cname'", "!define DOC_COMPONENT '$type'", "!DOC_COMPONENT_BEGIN"); } else { $SDF_USER'var{'DOC_COMPONENT'} = $type; } # Return the component name (needed for stdlib/mif.sdt) return $cname; } # # >>_Description:: # {{Y:_MifComponentName}} generates a name for a component, # given the basename of the document. # sub _MifComponentName { local($base) = @_; local($cname); $_mif_component_cntr++; $cname = $SDF_USER'var{'MIF_COMPONENT_PATTERN'}; $cname = '$b_$n.$o' if $cname eq ''; $cname =~ s/\$b/$base/g; $cname =~ s/\$n/$_mif_component_cntr/; $cname =~ s/\$o/$out_ext/; return $cname; } # # >>_Description:: # {{Y:_MifBookBuild}} builds a book from information collected # during the generation of components. # sub _MifBookBuild { local(*book, $base) = @_; # local(@result); local(@newbook); local(@batch); local($added_toc); local(@flds, %values, $i); local($mif_file, $type); # Build a new book table which includes the derived components. # As we go, we also: # * generate the mif for each derived component # * build a set of fmbatch commands which # generate binary documents for each part # * collect the set of mif files so we can delete them later. @newbook = ($book[0]); @batch = (); @sdf_book_files = (); $added_toc = 0; @flds = &TableFields($book[0]); for ($i = 1; $i <= $#book; $i++) { %values = &TableRecSplit(*flds, $book[$i]); $type = $values{'Type'}; if ($added_toc == 0 && $type ne 'front' && $type ne 'pretoc') { if ($SDF_USER'var{'DOC_TOC'}) { $mif_file = &_MifBookDerived($base, 'toc', 'Table of Contents'); &_MifBookAddPart(*newbook, *batch, $mif_file, 'toc'); push(@sdf_book_files, $mif_file); } if ($SDF_USER'var{'DOC_LOF'}) { $mif_file = &_MifBookDerived($base, 'lof', 'List of Figures'); &_MifBookAddPart(*newbook, *batch, $mif_file, 'lof'); push(@sdf_book_files, $mif_file); } if ($SDF_USER'var{'DOC_LOT'}) { $mif_file = &_MifBookDerived($base, 'lot', 'List of Tables'); &_MifBookAddPart(*newbook, *batch, $mif_file, 'lot'); push(@sdf_book_files, $mif_file); } $added_toc = 1; } # Add this part $mif_file = $values{'Part'}; &_MifBookAddPart(*newbook, *batch, $mif_file, $type); push(@sdf_book_files, $mif_file); } if ($SDF_USER'var{'DOC_IX'}) { $mif_file = &_MifBookDerived($base, 'ix', 'Index'); &_MifBookAddPart(*newbook, *batch, $mif_file, 'ix'); push(@sdf_book_files, $mif_file); } # Pass the batch commands to fmbatch &_MifRunBatch(*batch, $verbose); # Cleanup the MIF for each part &SDF_USER'SdfBookClean(); # Return result return &MifBook(*newbook, $SDF_USER'var{'OPT_NUMBER_PER_COMPONENT'}); } # # >>_Description:: # {{Y:_MifBookDerived}} creates a derived component for a book. # {{mainbase}} is the base component (e.g. ug_doc) of the main file. # {{type}} is the type of derived component (e.g. toc). # The {{default_title}} is used if the appropriate SDF variable is not set. # The name of the new file is returned. # sub _MifBookDerived { local($mainbase, $type, $default_title) = @_; local($newfile); local($upper_type, $tag, $title); local(@sdf_text, @mif_data); # Build the sdf $upper_type = "\U$type"; $tag = $upper_type . 'T'; $title = $SDF_USER'var{"DOC_${upper_type}_TITLE"}; $title = $default_title if $title eq ''; @sdf_text = ("$tag:$title"); # Convert the sdf to mif @mif_data = (); &_MifAddSection(*mif_data, *sdf_text); @mif_data = &_MifFinalise(*mif_data, $upper_type); # Output the mif $newfile = &NameJoin('', $mainbase . "_$type", $out_ext); if (open(DERIVED, ">$newfile")) { print DERIVED join("\n", @mif_data), "\n"; } else { &AppMsg("warning", "unable to create file '$newfile'"); } close(DERIVED); # Return result return $newfile; } # # >>_Description:: # {{_MifBookAddPart}} is the common processing required in {{_MifBookBuild}} # to add a part to a book for each mif file. # sub _MifBookAddPart { local(*newbook, *batch, $mif_file, $type) = @_; # local(); local($doc_file); $doc_file = &NameSubExt($mif_file, 'doc'); push(@newbook, &TableRecJoin(*flds, 'Part', $doc_file, 'Type', $type)); push(@batch, "Open \"$mif_file\""); push(@batch, "SaveAs d \"$mif_file\" \"$doc_file\""); push(@batch, "Quit \"$mif_file\""); #print STDERR "adding $doc_file,$type.\n"; } # # >>_Description:: # {{_MifRunBatch}} executes a set of fmbatch commands ({{@batch}}). # If {{verbose}} is set, the results are keep in a temporary file. # sub _MifRunBatch { local(*batch, $verbose) = @_; # local(); local($fmbatch, $tmp_file); $fmbatch = "fmbatch -i"; $tmp_file = "/tmp/sdf$$"; if (open(FMBATCH, "|$fmbatch > $tmp_file")) { printf FMBATCH "%s\n", join("\n", @batch); } else { &AppMsg("error", "failed to pipe data to fmbatch"); } close(FMBATCH); unlink($tmp_file) unless $verbose; } # # >>Description:: # {{Y:MifBook}} returns a mif book built from the components given in # {{@book}}. If {{number_per_component}} is set: # # * parts before the first chapter have roman page numbering # * remaining parts are numbered per section (i.e. 1-1, 1-2, etc.) # sub MifBook { local(*book, $number_per_component) = @_; local(@mif); local(@flds, %values, $i); local($partfile, $type); local($suffix, @tags, %settings); local($j); local($sectnum, $chapter_cnt, $appendix_cnt); # Build the title $title = sprintf(" # Generated by %s %s", $app_product_name, $app_product_version); @mif = ($title); # Add the paragraph catalog so that the PDF table of contents works #push(@mif, &_MifParasToText(*_mif_tpl_paras)); # Process the list of parts @flds = &TableFields($book[0]); $chapter_cnt = 0; $appendix_cnt = 0; for ($i = 1; $i <= $#book; $i++) { # Get the attributes %values = &TableRecSplit(*flds, $book[$i]); $partfile = $values{'Part'}; $type = $values{'Type'}; # For derived components, get the suffix, list of tags and settings. $suffix = ''; @tags = (); %settings = (); if ($type eq 'toc') { $suffix = 'TOC'; @tags = (); for ($j = 1; $j <= $SDF_USER'var{'DOC_TOC'}; $j++) { push(@tags, $SDF_USER'parastyles_to{"H$j"}, $SDF_USER'parastyles_to{"A$j"}, $SDF_USER'parastyles_to{"P$j"}); } push(@tags, $SDF_USER'parastyles_to{"LOFT"}); push(@tags, $SDF_USER'parastyles_to{"LOTT"}); push(@tags, $SDF_USER'parastyles_to{"IXT"}); } elsif ($type eq 'lof') { $suffix = 'LOF'; @tags = ($SDF_USER'parastyles_to{"FT"}); $settings{'StartPageSide'} = 'NextAvailableSide'; } elsif ($type eq 'lot') { $suffix = 'LOT'; @tags = ($SDF_USER'parastyles_to{"TT"}); $settings{'StartPageSide'} = 'NextAvailableSide'; } elsif ($type eq 'ix') { $suffix = 'IX'; @tags = ('Index'); %settings = (); } # If requested, number per component once a chapter is found if ($number_per_component) { if ($type eq 'chapter') { $sectnum = ++$chapter_cnt; } elsif ($type eq 'appendix') { $sectnum = sprintf("%c", ord('A') + $appendix_cnt++); } elsif ($type eq 'ix') { $sectnum = "Index"; } elsif (! $_MIF_FRONT_PART{$type}) { $sectnum = "\u$type"; } if ($sectnum ne '') { # Note: "\\x15 " is FrameMaker's nonbreaking hyphen $settings{'PageNumbering'} = 'Restart'; $settings{'PageNumPrefix'} = "$sectnum\\x15 "; } } # Build result push(@mif, &MifBookComponent($partfile, $suffix, *tags, %settings)); } # Add a closing comment (to match output as generated by Frame) push(@mif, "# End of Book"); # Return result return @mif; } # # >>Description:: # {{Y:MifBookComponent}} returns a book component object # for {{file}}. If {{suffix}} is supplied, a derived book # component is returned which includes derived tags for each # member in {{@derived}}. The book attributes can be # changed from the defaults using {{attrs}}. No checking # is done on {{attrs}} so take care. # # >>Limitations:: # {{Y:MifBookComponent}} currently assumes the referenced file will # be in the same directory as the output. i.e. only the file's # local name (including extension) is stored. # sub MifBookComponent { local($file, $suffix, *derived, %attrs) = @_; local(@result); local(%book_attrs); local($dir, $base, $ext, $short_name, $tag); local($derive_type); # Generate the book attributes to be used %book_attrs = %_MIF_DFLT_BOOK_ATTRS; @book_attrs{keys %attrs} = values %attrs; # Build the header ($dir, $base, $ext) = &NameSplit($file); $short_name = &NameJoin('', $base, $ext); @result = ("%s'>", $short_name)); # Add the derived stuff, if any $derive_type = $suffix eq 'IX' ? 'IDX' : $suffix; if ($suffix) { push(@result, " ", " ", " "); for $tag (@derived) { push(@result, " "); } } # Add the attributes push(@result, sprintf(" ", $book_attrs{'StartPageSide'}), sprintf(" ", $book_attrs{'PageNumbering'}), sprintf(" ", $book_attrs{'PgfNumbering'}), sprintf(" ", $book_attrs{'PageNumPrefix'}), sprintf(" ", $book_attrs{'PageNumSuffix'}), sprintf(" ", $book_attrs{'DefaultPrint'}), sprintf(" ", $book_attrs{'DefaultApply'})); # Add more derived stuff, if applicable. # Note that we add this here rather than above # to keep the same ordering as Frame uses. if ($suffix) { push(@result, " "); } # End the object push(@result, "> # end of BookComponent"); # Return result return @result; } # package return value 1;