! *viewer/report.pvc - Report Object
! By Gord Davey
! (c) Copyright 2004-2005 Sage Software Canada Ltd.
! Version Info:
! Source Code File: $RCSfile: report.pvs,v $   Version: $Revision: 1.62 $
!      Last Edited: $Date: 2006/08/09 20:55:03 $   Author: $Author: gordd $
!
! This object contains the logic to retrive and output reports
!
! Instantiate with args NEW("*viewer/report", [ FileName ] | [ ObjectHandle ] )
!
def class "*viewer/report" create OnCreate required delete OnDelete required

    property Version$ = mid(mid("$Revision: 1.62 $",12), 1, len(mid("$Revision: 1.62 $",12)) - 2)
    precision 6

    local This                             ! Variable with this objects handle in it.
    local BITMAP, WINPRT, PDF              ! Constants
    local Software$
    local Sheets                           ! Physical pages are kept in "Sheets" grouping object
    local Info                             ! Informational object
    local FileIsObject                     ! FileName passed is of type 0=Name or 1=Object
    local FileType                         ! 1=Object, 2=Binary, 3=Binary w/Banner, 4=Keyed, 5=Indexed
    local FileChan                         ! Channel Number of File (if any)
    local Lockable                         ! Whether or not the file can be locked (is generating program finished with it or not)
    local FileIsReadOnly                   !
    local Buffer$                          ! Buffer to hold excess data from binary files
    local LoadPointer, FetchMore, EOF, BannerLoaded
    local HasLeadingBlankPage, HasOtherBlankPages
    local PortraitFontSize, LandscapeFontSize ! Only used for Text based reports
    local HasSPCP
    local TabSize
    local SPCols, SPRows, SPFont$
    local CPCols, CPRows, CPFont$
    local EPCols, EPRows, EPFont$

    local Metric                           ! Imperial or Metric measurements
    local PageLoaded                       ! Total number of pages loaded

    property DefaultPage object            set err ! Default Page Object
    property WaterMark                     set err ! Don't allow people to set this externally

    property Page                          ! Current Page
    property Pages     = Sheets'Count      ! Total # of Page objects

    property Spooled                       set err ! Spooling State 1=Complete | 0=Incomplete
    property Spool                         ! Set to 0 to pause spooling, set to 1 to continue spooling

    property Title$                        ! Set the title if you wish

    property FileName$                     set err ! Path Name of Report File, set during NEW()
    property FileName                      set err ! Object Handle of Report Object, set during NEW()

    property SuppressFirstBlankPage        ! 1=Suppress leading blank pages (default), 0=No suppression
    property SuppressAllBlankPages         ! 1=Suppress blank pages (default), 0=No suppression
    property SuppressWatermarks            ! 1=Report Requests all watermarks be suppressd

    property DefaultFixedFont$             set SetDefaultFixedFont ! Contains the name of the Fixed Width Font to use, Defaults to Courier New
    property DefaultFixedFontSize          set SetDefaultFixedFontSize ! Contains the point size of the Default Fixed Width Font to use. 0=Calc based on ReportColumns/Lines
    property MinimumColumns                ! Minimum number of columns for report ie: 132 columns
    property MinimumRows                   ! Minimum number of lines for report  ie: 66 lines
                                           ! 0=Auto Calc based on number of lines in first non-blank page
    property PaginationAt                  ! 0=Contains FormFeeds, >0 Line number to paginate at
    property ScaleToFit                    ! Default = 0, If 1 a font size (larger or smaller) will be selected so that the report fills the whole page

    property ReportType                    set err ! 1 = Text with FF, 2 = Text without FF, 3 = GUI (must have FF)

    property Printer$                      ! The Report may have a specific printer it will print to.
    property DPI                           ! Printer Resolution, 0 = printer default
    property ScreenDPI
    property Range$                        ! "All" or "StartPage-EndPage"
    property Collate                       ! 0=No Collation, 1=Yes Collate (default)
    property Copies                        ! Number of copies to print, default of 1
    property Colour                        ! 0=Monochrome, 1=Colour
    property Color = Colour                !

    property Converter object              ! A Customizable Data Converter

    property Search                        ! 0 = Inactive, 1 = Active

    function Search(Text$, CaseSensitive, Direction, Page, XLocation, YLocation)   SearchForText
    function SearchReset()  SearchReset
    function local SearchInit() SearchInit
    function local SearchPage(SearchPage, SearchText$, SearchCase)  SearchPage
    local SearchText$, SearchTable, SearchTable$, SearchOccurs, SearchPage, \
          SearchCase, SearchedPage, SearchLastOccur

    function GetInfo()                     ! Gets the number of informational items for report
        return Info'Items

    function GetInfo(Item%, Name$, Value$)      GetInformation

    function Load()                             LoadFile
    function Page()                             GetPage
    function Page(PageNum%)                     GetPage
    function RenderPage(Channel%)               RenderPage
    function RenderBlankPage(Channel%)          RenderBlankPage

    function local OpenFile(FileName$)          OpenFile
    function local CheckLockable()              CheckLockable
    function local DetermineReportType()        DetermineReportType
    function local DetermineReportSizing()      DetermineReportSizing
    function local IsPageBlank(String$)         IsPageBlank
    function local LoadBannerInfo(String$)      LoadBannerInfo

    function local GetPageData(Page%, PageData$) GetPageData

    function local ScanForAttributes(PageData$, PaperSize, Orientation, PaperBin, \
                                   DefaultFont$, DefaultFontSize, DefaultFontAttributes$, \
                                   DefaultFontRotation, CurrentFont$, CurrentFontSize, \
                                   CurrentFontAttributes$, CurrentFontRotation, \
                                   LPI, CPI, ForegroundColour$, BackgroundColour$, Mode$, \
                                   Pen$, Fill$, UnderLine, ReverseVideo, \
                                   MarginLeft, MarginTop, MarginRight, MarginBottom, \
                                   SPCPMode, PlusS) ScanForAttributes

    function local GetPageAttributes(Page, PaperSize, Orientation, PaperBin, \
                                   DefaultFont$, DefaultFontSize, DefaultFontAttributes$, \
                                   DefaultFontRotation, CurrentFont$, CurrentFontSize, \
                                   CurrentFontAttributes$, CurrentFontRotation, \
                                   LPI, CPI, ForegroundColour$, BackgroundColour$, Mode$, \
                                   Pen$, Fill$, UnderLine, ReverseVideo, \
                                   MarginLeft, MarginTop, MarginRight, MarginBottom, \
                                   SPCPMode, PlusS) GetPageAttributes

    function local SetPageAttributes(Page, PaperSize, Orientation, PaperBin, \
                                   DefaultFont$, DefaultFontSize, DefaultFontAttributes$, \
                                   DefaultFontRotation, CurrentFont$, CurrentFontSize, \
                                   CurrentFontAttributes$, CurrentFontRotation, \
                                   LPI, CPI, ForegroundColour$, BackgroundColour$, Mode$, \
                                   Pen$, Fill$, UnderLine, ReverseVideo, \
                                   MarginLeft, MarginTop, MarginRight, MarginBottom, \
                                   SPCPMode, PlusS) SetPageAttributes

    function local OutputPageAttributes(Page%, Channel%) OutputPageAttributes

    function local HighlightSearchItem(PageData$, Element) HighlightSearchItem

    function local IsGUIDevice(Channel%)
        enter Channel%
        tmp$ = ucs(pth(Channel%))
        if mid(tmp$,1,5)="[WDX]" \
            then tmp$=tmp$(6)

        if mid(tmp$, 1, 8) = "*BITMAP*" \
            then return BITMAP
        if mid(tmp$, 1, 8) = "*WINPRT*" \
            then return WINPRT
        if mid(tmp$, 1, 5) = "*PDF*" \
            then return PDF
        return 0

fnend
!
!
! Method: RenderPage(Channel%)
! Must set Page before calling this method
! This is the routine which will take the current Page's data and output it to the channel
! number given.
! Its here that you could modify the data that is retrieved from disk using GetPageData()
! and modify it, before it gets output to the channel. Note that the channel may be
! either a *BITMAP* or a physical printer, like *WINPRT* or *WINDEV* or other.
RenderPage: ! 0=Sucessfull Page render, Non-Zero = Failed Render
    enter (ChannelNum%)

    if Page = 0 \
        then return -1

    ! Retrieve the data for this page
    This'GetPageData(Page, PageData$)

    if mid(PageData$, -3) = $1B4A53$ or \
       mid(PageData$, -3) = $1B4A44$ or \
       mid(PageData$, -3) = $1B4A4E$ \
        then PageData$ = PageData$(1, len(PageData$) - 3)

    if mid(PageData$, -1) = $0a$ \
        then PageData$ = PageData$(1, len(PageData$) - 1)

    if pos($0d0a$ : mid(PageData$, -1), 1) = 0 \
        then PageData$+=$0d$
    ! Setup the default state for this page to start with
    This'OutputPageAttributes(Page, ChannelNum%)

    if This'IsGUIDevice(ChannelNum%) = BITMAP and Page = SearchedPage and SearchLastOccur > 0 then \
    {   Highlighted = not(This'HighlightSearchItem(PageData$, SearchLastOccur))
    }

    if HasSPCP and This'IsGuiDevice(ChannelNum%) then \
    {   PageData$ = sub(PageData$, esc+"SP", SPFont$)
        PageData$ = sub(PageData$, esc+"CP", CPFont$)
        PageData$ = sub(PageData$, esc+"EP", EPFont$)
    }

    if Converter then \
    {
        This'GetPageAttributes(Page - 1, PSize, POrient, PBin, DFont$, DFontSize, \
                               DFontAttrib$, DFontRot, CFont$, CFontSize, CFontAttrib$, \
                               CFontRot, LPI, CPI, FColour$, BColour$, CMode$, Pen$, \
                               Fill$, UnderLine, ReverseVideo, MLeft, MTop, MRight, MBottom, \
                               SPCPMode, PlusS)
        Converter'SetAttributes(ReportType, PSize, POrient, PBin, DFont$, DFontSize, \
                                DFontAttrib$, DFontRot, CFont$, CFontSize, CFontAttrib$, \
                                CFontRot, LPI, CPI, FColour$, BColour$, CMode$, Pen$, \
                                Fill$, UnderLine, ReverseVideo, MLeft, MTop, MRight, MBottom, \
                                SPCPMode, PlusS, SPFont$, CPFont$, EPFont$)
        Converter'Convert(Page, PageData$, This'IsGuiDevice(ChannelNum%))
    }

    ! print the page data which is held in PageData$
    print (ChannelNum%, err=RenderErr) '-B',PageData$,'+B',

    return 0

    RenderErr:
    return err
!
!
RenderBlankPage:
    enter (ChannelNum%)

    if Page = 0 \
        then return -1

    BlankText$ = msg("TBlankPageText")
    BlankTextFont$ = msg("TBlankPageTextFont")

    ! Retrieve the data for this page
    This'GetPageData(Page, PageData$)

    ! Setup the default state for this page to start with
    This'OutputPageAttributes(Page, ChannelNum%)

    if This'IsGUIDevice(ChannelNum%) then \
    {
        x = mxc(ChannelNum%) + 1
        y = mxl(ChannelNum%) + 1

        w = txw(BlankText$, ChannelNum%, fnt=BlankTextFont$)
        h = txh(BlankText$, ChannelNum%, fnt=BlankTextFont$)

        x = x / 2
        y = y / 2
        w = w / 2
        h = h / 2

        print (ChannelNum%, err=*proceed)'font'(BlankTextFont$), \
              'text'(@x(x + 3) - w, @y(y) - h, BlankText$),

    } else \
    {
        x = mxc(ChannelNum%) + 1
        y = mxl(ChannelNum%) + 1

        x = int((x - len(BlankText$)) / 2)
        y = int((y - 1) / 2)

        print (ChannelNum%, err=RenderErr) @(x, y), BlankText$,
    }

    return 0
!
!
! Method: Load()
! Called to load the report, or to continue loading, will be called many times
! until the Spooled status changes to 1. If Spool = 0 then spooling is paused
! Maximum 50,000 pages, if using objects as pages, depending on the Code.
! Info to appear in the banner can be added by:
!   Info'SetItem(ItemName$, Value$)
!   Info'SetItem("Author","Gord Davey")
!   Info'SetItem("Company","Sage Software Canada Ltd.")
LoadFile:
    ! If the report is already spooled, or spooling paused then simply exit
    if Spooled or Spool = 0 then return 1
    ! Check to see if app generating report is finished
    if not(FileIsObject) and not(Lockable) then This'CheckLockable()
    ! Retrieve next page of info based on what sort of file we are dealing with
    FetchMore = 1
    LoadFileRedoPage:

    switch FileType
        case 1 ! Its an Object
            break

        ! Tries to find pages by FormFeeds first, hopefully there is one with the first 64K
        ! of the file (just over 255x255 cells)
        ! Fall back to paginating by line feeds if there is no FormFeeds
        case 2 ! Binary File
            PageLocType = 2

            if FetchMore then \
            {   EOF = 1
                read record(FileChan, ind=LoadPointer + len(Buffer$), siz=65536, end=*next) record$;
                    if Lockable and len(record$) = 0 \
                        then EOF = 1 \
                        else Buffer$ += record$, EOF = 0
                if ReportType = 0 then \
                {   if not(This'DetermineReportType()) \
                        then FetchMore = 1; break
                    if ReportType and ReportType < 3 \
                        then This'DetermineReportSizing()
                }

                FetchMore = 0
            }

            if ReportType = 1 or ReportType = 3 then \
            {   ! Try to find a page by a form feed
                hasFF = pos($0C$ = Buffer$, 1)
                if hasFF then \
                {   PageData$ = Buffer$(1, hasFF - 1)
                    Buffer$ = Buffer$(hasFF + 1)
                    NumBytes = len(PageData$)
                    PageLoc$ = str(LoadPointer)+":"+str(NumBytes)
                    LoadPointer += hasFF
                    break
                } else \
                {   FetchMore = 1
                }
            }

            if ReportType = 2 then \
            {   ! Try to find a page by number of Line Feeds
                numLF = pos($0A$ = Buffer$, 1, PaginationAt)
                if numLF then \
                {   PageData$ = Buffer$(1, numLF - 1)
                    Buffer$ = Buffer$ (numLF + 1)
                    NumBytes = len(PageData$)
                    PageLoc$ = str(LoadPointer)+":"+str(NumBytes)
                    LoadPointer += numLF
                    break
                }
            }
            ! Check to see if there is any sort of last page info and drain it if there is
            if EOF and Lockable and PageData$ = "" then \
            {   PageData$ = Buffer$
                PageLoc$ = str(LoadPointer)+":"+str(len(PageData$))
                LoadPointer += len(Buffer$) + 1
                Buffer$ = ""
                FetchMore = 0
            }

            break

        case 3 ! Binary with Banner

            PageLocType = 2

            if FetchMore then \
            {   EOF = 1
                read record(FileChan, ind=LoadPointer + len(Buffer$), siz=65536, end=*next) record$;
                    if Lockable and len(record$) = 0 \
                        then EOF = 1 \
                        else Buffer$ += record$, EOF = 0
                if ReportType = 0 then \
                {   if not(This'DetermineReportType()) \
                        then FetchMore = 1; break

                    if BannerLoaded = 0 and PageLoaded = 0 and pos($0c$ = Buffer$, 1, 0) >= 1 then \
                    {   This'LoadBannerInfo(Buffer$(1, pos($0c$ = Buffer$, 1) - 1))

                        x = pos($0C$ = Buffer$, 1) + 1
                        Buffer$ = Buffer$(x), LoadPointer = x - 1
                        if mid(Buffer$, 1, 2) = $0D0a$ \
                            then Buffer$ = Buffer$(3), LoadPointer += 2 \
                            else if mid(Buffer$, 1, 1) = $0a$ \
                                     then Buffer$ = Buffer$(2), LoadPointer +=1 \
                                     else if mid(Buffer$, 1, 1) = $0d$ \
                                              then Buffer$ = Buffer$(2), LoadPointer += 1
                    }

                    if ReportType and ReportType < 3 then \
                    {   ! Trim the Font/CPI/LPI inserted by *dev/preview
                        ! Simple Text Reports do not need it
                        x = pos(esc+"mn$20020258"+esc+"JS"+esc+"mn$210203E8" = Buffer$, 1)
                        if x then \
                        {   Buffer$ = Buffer$(x + 27)
                            LoadPointer += x + 26
                        }
                        This'DetermineReportSizing()
                    }
                }

                FetchMore = 0
            }

            if ReportType = 1 or ReportType = 3 then \
            {   ! Try to find a page by a form feed
                hasFF = pos($0C$ = Buffer$, 1)
                if hasFF then \
                {   PageData$ = Buffer$(1, hasFF - 1)
                    Buffer$ = Buffer$(hasFF + 1)
                    NumBytes = len(PageData$)
                    PageLoc$ = str(LoadPointer)+":"+str(NumBytes)
                    LoadPointer += hasFF
                    break
                } else \
                {   FetchMore = 1
                }
            }

            if ReportType = 2 then \
            {   ! Try to find a page by number of Line Feeds
                numLF = pos($0A$ = Buffer$, 1, PaginationAt)
                if numLF then \
                {   PageData$ = Buffer$(1, numLF - 1)
                    Buffer$ = Buffer$ (numLF + 1)
                    NumBytes = len(PageData$)
                    PageLoc$ = str(LoadPointer)+":"+str(NumBytes)
                    LoadPointer += numLF
                    break
                }
            }
            ! Check to see if there is any sort of last page info and drain it if there is
            if EOF and Lockable and PageData$ = "" then \
            {   PageData$ = Buffer$
                PageLoc$ = str(LoadPointer)+":"+str(len(PageData$))
                LoadPointer += len(Buffer$) + 1
                Buffer$ = ""
                FetchMore = 0
            }

            break

        case 4 ! Keyed
            break

        case 5 ! Indexed
            break

    esac

    ! if this pass fetched nothing and we're not at the end of the file, then get out
    if not(EOF) and PageLoaded > 0 and lockable and PageData$ = "" and hasFF = 0 then \
    {   FetchMore = 1
    }
    ! Dont forget to drain the remainder
    if EOF and lockable and PageData$ = "" and hasFF = 0 and Buffer$ > "" then \
    {   FetchMore = 1
    }

    if FetchMore \
        then return 1

    ! if we have nothing and the file is fully spooled and scanned
    if EOF and Lockable and Buffer$ = "" and (PageData$ = "" or PageData$ = $1B2A43$ or (ReportType = 3 and PageData$ = $0D0A1B2A43$)) then \
    {   Spooled = 1
        Spool = 0
        FetchMore = 0
        if PageLoaded = 0 and (PageData$ = "" or PageData$ = $1B2A43$ or (ReportType = 3 and PageData$ = $0D0A1B2A43$)) then \
            return 1
        if HasLeadingBlankPage \
            then Info'SetItem("First Page","Suppressed, Is Blank")
        if HasOtherBlankPages \
            then Info'SetItem("Other Blank Pages","Has "+str(HasOtherBlankPages)+" other suppressed blank page"+tbl(HasOtherBlankPages > 1,"","s")+".")
        return 1
    }

    ! PageData$ will have all the data for this particular page
    IsBlank = This'IsPageBlank(PageData$)
    if SuppressFirstBlankPage and PageLoaded = 0 and IsBlank \
        then HasLeadingBlankPage = 1; PageData$ = ""; return 1
    if SuppressAllBlankPages and IsBlank \
        then HasOtherBlankPages += 1; PageData$ = ""; return 1

    PageLoaded++

    This'GetPageAttributes(PageLoaded - 1, PSize, POrient, PBin, DFont$, DFontSize, \
                    DFontAttrib$, DFontRot, CFont$, CFontSize, CFontAttrib$, CFontRot, LPI, CPI, \
                    FColour$, BColour$, CMode$, Pen$, Fill$, UnderLine, ReverseVideo, MLeft, MTop, MRight, MBottom, SPCPMode, PlusS)

    OriginalDFont$ = DFont$
    OriginalDFontSize = DFontSize

    This'ScanForAttributes(PageData$, PSize, POrient, PBin, DFont$, DFontSize, \
                    DFontAttrib$, DFontRot, CFont$, CFontSize, CFontAttrib$, CFontRot, LPI, CPI, \
                    FColour$, BColour$, CMode$, Pen$, Fill$, UnderLine, ReverseVideo, MLeft, MTop, MRight, MBottom, SPCPMode, PlusS)

    if ReportType = 2 and (DFont$ <> OriginalDFont$ or DFontSize <> OriginalDFontSize) then \
    {
        ! Here's where we get tricky, they changed the font size, and are counting on
        ! the auto line feed trip to eject a page, but for *bitmap* it just clears so
        ! we need to figure something out. Work out a new PaginationAt value and
        ! send the page back to be carved up into different size slices.
        OriginalPaginationAt = PaginationAt

        if not(pos("WIN"=ucs(SYS),1)) and tcb(88) then IsWindX$ = "[WDX]"
        FontFunc = new(IsWindX$ + "*viewer/fontfunctions")

        if ScreenDPI \
            then FontFunc'DPI = ScreenDPI

        PageObj = This'Page(PageLoaded - 1)
        width = PageObj'PaperWidth
        length = PageObj'PaperLength
        margins$ = str(MLeft) + ":" + str(MTop) + ":" + str(MRight) + ":" + str(MBottom)

        bitmap = FontFunc'CreatePageAsBitmap(width, length, margins$)
        FontFunc'PrintPageAsBitmap(bitmap,'font'(DFont$,DFontSize,DFontAttrib$)+'DF')
        if CPI \
            then FontFunc'PrintPageAsBitmap(bitmap,'cpi'(CPI))
        if LPI \
            then FontFunc'PrintPageAsBitmap(bitmap,'lpi'(LPI))

        Maxs$ = FontFunc'GetMaximums$(bitmap)
        read data from Maxs$,sep=":" to *,PaginationAt,*,*,*,*

        drop object FontFunc

        if PaginationAt <> OriginalPaginationAt then \
        {   if numLF \
                then LoadPointer -= numLF \
                else LoadPointer = LoadPointer - len(PageData$) - 1

            if Buffer$ > "" \
                then Buffer$ = PageData$ + $0a$ + Buffer$ \
                else Buffer$ = PageData$

            PageData$ = ""
            PageLoaded--
            goto LoadFileRedoPage
        }

    }
    ! Add a new Page object and set its values
    NewPage = This'Sheets'AddNew()
    NewPage'Metric = Metric, \
    NewPage'DataType = PageLocType, \
    NewPage'Data$ = PageLoc$

    This'SetPageAttributes(PageLoaded, PSize, POrient, PBin, DFont$, DFontSize, \
                    DFontAttrib$, DFontRot, CFont$, CFontSize, CFontAttrib$, CFontRot, LPI, CPI, \
                    FColour$, BColour$, CMode$, Pen$, Fill$, UnderLine, ReverseVideo, MLeft, MTop, MRight, MBottom, SPCPMode, PlusS)

    return 1
!
!
! This next method will return a 1 if the page is blank, 0 if it has data
IsPageBlank:
    enter (Value$)
    if Value$ = "" \
        then return 1
    if Value$ =  $1B2A43$ \
        then return 1
    if ReportType = 3 and Value$ =  $0D0A1B2A43$ \
        then return 1
    if ReportType = 3 then \
    {   ! First Check, Does it contain GUI Mnemonics ? TEXT Short, TEXT Long, PICTURE, RECTANGLE, LINE
        if pos($1B6D6E243141$ = Value$, 1) \
            then return 0
        if pos($1B6D78243141$ = Value$, 1) \
            then return 0
        if pos($1B6D6E243038$ = Value$, 1) \
            then return 0
        if pos($1B6D6E243041$ = Value$, 1) \
            then return 0
        if pos($1B6D6E243039$ = Value$, 1) \
            then return 0
    }
    ! Next check for Text items
    Value$ = stp(mnemonic Value$) ! remove every mnemonic
    Value$ = sub(Value$,$0D$,"") ! Remove 'CR'
    Value$ = sub(Value$,$0A$,"") ! Remove 'LF'
    Value$ = stp(Value$,2) ! Remove spaces
    ! If there is anything left it should be text for the text plane.
    if Value$ = "" \
        then return 1
    return 0
!
!
! The next method tries to figure out report sizing for Text based Reports.
! Figures out Font, Font Size, Max Columns per row, Max Lines per page, and pagination characteristics
! Only Text based reports need do this.
! ReportType of 1 is Text with FF's, ReportType 2 is Text with LF's only.
DetermineReportSizing:
    maxcolumns = 0
    maxrows = 0

    if pos(esc + "CP" = Buffer$, 1) \
        then HasCP = 1, HasSPCP = 1
    if pos(esc + "SP" = Buffer$, 1) \
        then HasSP = 1, HasSPCP = 1
    if pos(esc + "EP" = Buffer$, 1) \
        then HasEP = 1, HasSPCP = 1

    Value$ = stp(mnemonic Buffer$)
    Value$ = sub(Value$, $0D0A$, $0A$)
    ! If the first page is totally blank then skip that in our calcs
    if mid(Value$, 1, 1) = $0C$ \
        then Value$ = Value$(2)

    StartingFontSize = DefaultFixedFontSize

    lf1 = 1
    if ReportType = 1 then \
    {   ! look at just the first 3 pages non blank pages or however much is buffered
        x = pos($0c$ = Value$, 1, 3)
        if x then Value$ = Value$ (1, x - 1)
        ! Figure out the longest line in these first few pages
        x = len(Value$)
        z = 1
        while z <= x
            y = pos($0a0c0d$ : Value$(z), 1)
            if y = 0 \
                then y = len(Value$(z)) + 1
            maxcolumns = max(maxcolumns, y - 1)
            z += y
        wend

        ! Figure out the maximum number of rows on each of these first few pages
        lf1 = 1
        y = pos($0c$ = Value$ + $0c$, 1, 0)
        for x = 1 to y
            lf2 = pos($0C$ = Value$ + $0c$, 1, x)
            maxrows = max(maxrows, pos($0A$ = Value$(lf1, lf2 - lf1), 1, 0))
            lf1 = lf2 + 1
        next x
        maxrows += 1

        if HasCP and SPCols = 0 and CPCols = 0 \
            then CPCols = max(maxcolumns, 132)
        if not(HasCP) and SPCols = 0 \
            then SPCols = max(maxcolumns, 80)
        if CPCols and not(SPCols) \
            then SPCols = int(CPCols) / 1.65
        if SPCols = 0 \
            then SPCols = max(maxcolumns, 80)
        if CPCols = 0 \
            then CPCols = int(SPCols * 1.65)
        if EPCols = 0 \
            then EPCols = int(SPCols / 2)

        if (HasSP or HasCP or HasEP) and SPCols \
            then maxcolumns = SPCols
        if (HasSP or HasCP or HasEP) and SPRows = 0 \
            then SPRows = maxrows


        if MinimumColumns and ScaleToFit \
            then MinimumColumns = maxcolumns
        if MinimumRows and ScaleToFit \
            then MinimumRows = maxrows

        if MinimumColumns = 0 and not(ScaleToFit) then \
        {   if maxcolumns <= 80 \
                then MinimumColumns = 80
            if maxcolumns > 80 and maxcolumns <= 132 \
                then MinimumColumns = 132
            if maxcolumns > 132 and maxcolumns <= 235 \
                then MinimumColumns = 235
            if maxcolumns > 235 \
                then MinimumColumns = 255
        }

        if MinimumRows = 0 \
            then MinimumRows = maxrows

        if MinimumColumns = 0 \
            then MinimumColumns = maxcolumns

        if ScaleToFit \
            then StartingFontSize = -40
    }

    if ReportType = 2 then \
    {   ! look at just the first 255 lines or however much is buffered
        x = pos($0a$ = Value$, 1, 255)
        if x then Value$ = Value$ (1, x - 1)
        ! Figure out the longest line in the first 255 or so lines
        x = len(Value$)
        z = 1
        while z <= x
            y = pos($0a0d$ : Value$(z), 1)
            if y = 0 \
                then y = len(Value$(z)) + 1
            maxcolumns = max(maxcolumns, y - 1)
            z += y
        wend

        if HasCP and SPCols = 0 and CPCols = 0 \
            then CPCols = max(maxcolumns, 132)
        if not(HasCP) and SPCols = 0 \
            then SPCols = max(maxcolumns, 80)
        if CPCols and not(SPCols) \
            then SPCols = int(CPCols) / 1.65
        if SPCols = 0 \
            then SPCols = max(maxcolumns, 80)
        if CPCols = 0 \
            then CPCols = int(SPCols * 1.65)
        if EPCols = 0 \
            then EPCols = int(SPCols / 2)

        if (HasSP or HasCP or HasEP) and SPCols \
            then maxcolumns = SPCols

        if MinimumColumns and ScaleToFit \
            then MinimumColumns = maxcolumns

        if MinimumColumns = 0 and not(ScaleToFit) then \
        {   if maxcolumns <= 80 \
                then MinimumColumns = 80
            if maxcolumns > 80 and maxcolumns <= 132 \
                then MinimumColumns = 132
            if maxcolumns > 132 and maxcolumns <= 235 \
                then MinimumColumns = 235
        }

        if MinimumColumns = 0 \
            then MinimumColumns = maxcolumns

        if ScaleToFit \
            then StartingFontSize = -40

    }

    if not(pos("WIN"=ucs(SYS),1)) and tcb(88) then IsWindX$ = "[WDX]"

    orient = DefaultPage'Orientation
    DefaultPage'Orientation = 0

    FontFunc = new(IsWindX$ + "*viewer/fontfunctions")

    if ScreenDPI \
        then FontFunc'DPI = ScreenDPI

    s = StartingFontSize

    PortraitFontSizes$ = FontFunc'GetSizesForPage$(DefaultFixedFont$, StartingFontSize, \
                                DefaultPage'PaperWidth, DefaultPage'PaperLength, \
                                str(DefaultPage'MarginLeft)+":"+str(DefaultPage'MarginTop)+":"+ \
                                str(DefaultPage'MarginRight)+":"+str(DefaultPage'MarginBottom))

    PortraitPaperWidth = DefaultPage'PaperWidth - (DefaultPage'MarginLeft + DefaultPage'MarginRight) / 1000
    PortraitPaperLength = DefaultPage'PaperLength - (DefaultPage'MarginTop + DefaultPage'MarginBottom) / 1000

    StartingFontSize = s

    LandscapeFontSizes$ = FontFunc'GetSizesForPage$(DefaultFixedFont$, StartingFontSize, \
                                DefaultPage'PaperLength, DefaultPage'PaperWidth, \
                                str(DefaultPage'MarginLeft)+":"+str(DefaultPage'MarginTop)+":"+ \
                                str(DefaultPage'MarginRight)+":"+str(DefaultPage'MarginBottom))

    LandscapePaperWidth = DefaultPage'PaperLength - (DefaultPage'MarginLeft + DefaultPage'MarginRight) / 1000
    LandscapePaperLength = DefaultPage'PaperWidth - (DefaultPage'MarginTop + DefaultPage'MarginBottom) / 1000

    drop object FontFunc

    DefaultPage'Orientation = orient

    NumSizes = pos(","=PortraitFontSizes$, 1, 0) + 1
    dim fonts$[1 : NumSizes]
    read data from PortraitFontSizes$,sep="," to fonts${all}

    PortraitFontSize = 0
    PortraitFontCols = 0
    PortraitFontRows = 0
    for x = 1 to NumSizes
        read data from fonts$[x],sep="=" to FontSize, FontInfo$
        read data from FontInfo$,sep=":" to FontCols, FontRows

        if PortraitFontSize = 0 and ReportType = 1 and \
           FontCols >= MinimumColumns and FontRows >= MinimumRows then \
        {   PortraitFontSize = FontSize
            PortraitFontCols = FontCols
            PortraitFontRows = FontRows
        }

        if PortraitFontSize = 0 and ReportType = 2 and \
           FontCols >= MinimumColumns then \
        {   PortraitFontSize = FontSize
            PortraitFontCols = FontCols
            PortraitFontRows = FontRows
        }

    next x

    if ReportType = 2 and DefaultPage'Orientation = 0 then \
    {   if ScaleToFit \
            then PaginationAt = max(MinimumRows, PortraitFontRows)
        if PaginationAt = 0 and MinimumRows = 0 \
            then PaginationAt = PortraitFontRows
        if PaginationAt = 0 and MinimumRows > 0 \
            then PaginationAt = MinimumRows
        if SPRows = 0 \
            then SPRows = PaginationAt, CPRows = SPRows, EPRows = SPRows
    }

    NumSizes = pos(","=LandscapeFontSizes$, 1, 0) + 1
    dim fonts$[1 : NumSizes]
    read data from LandscapeFontSizes$,sep="," to fonts${all}

    LandscapeFontSize = 0
    LandscapeFontCols = 0
    LandscapeFontRows = 0
    for x = 1 to NumSizes
        read data from fonts$[x],sep="=" to FontSize, FontInfo$
        read data from FontInfo$,sep=":" to FontCols, FontRows

        if LandscapeFontSize = 0 and ReportType = 1 and FontCols > MinimumColumns and FontRows > MinimumRows \
            then LandscapeFontSize = FontSize, LandscapeFontCols = FontCols, LandscapeFontRows = FontRows
        if LandscapeFontSize = 0 and ReportType = 2 and FontCols > MinimumColumns \
            then LandscapeFontSize = FontSize, LandscapeFontCols = FontCols, LandscapeFontRows = FontRows

    next x

    if ReportType = 2 and DefaultPage'Orientation = 1 then \
    {   if ScaleToFit \
            then PaginationAt = max(MinimumRows, LandscapeFontRows)
        if PaginationAt = 0 and MinimumRows = 0 \
            then PaginationAt = LandscapeFontRows
        if PaginationAt = 0 and MinimumRows > 0 \
            then PaginationAt = MinimumRows
        if SPRows = 0 \
            then SPRows = PaginationAt, CPRows = SPRows, EPRows = SPRows
    }

    if DefaultPage'Orientation = 0 then \
    {   DefaultPage'DefaultFont$ = DefaultFixedFont$
        DefaultPage'DefaultFontSize = PortraitFontSize
        DefaultFixedFontSize = PortraitFontSize
        if SPRows = 0 \
            then SPRows = PortraitPaperLength * 6 !(Make it 6 LPI)
        FontSizes$ = PortraitFontSizes$
        Length = PortraitPaperLength
        Width = PortraitPaperWidth
    } else \
    {   DefaultPage'DefaultFont$ = DefaultFixedFont$
        DefaultPage'DefaultFontSize = LandscapeFontSize
        DefaultFixedFontSize = LandscapeFontSize
        if SPRows = 0 \
            then SPRows = LandscapePaperLength * 6 !(Make it 6 LPI)
        FontSizes$ = LandscapeFontSizes$
        Length = LandscapePaperLength
        Width = LandscapePaperWidth
    }

    if CPRows = 0 \
        then CPRows = SPRows
    if EPRows = 0 \
        then EPRows = SPRows

    NumSizes = pos(","=FontSizes$, 1, 0) + 1
    dim fonts$[1 : NumSizes]
    read data from FontSizes$,sep="," to fonts${all}

    SPFont$ = ""
    CPFont$ = ""
    EPFont$ = ""
    for x = 1 to NumSizes
        read data from fonts$[x],sep="=" to FontSize, FontInfo$
        read data from FontInfo$,sep=":" to FontCols, FontRows

        if SPFont$ = "" and FontCols >= SPCols and FontRows >= SPRows \
            then SPFont$ = 'font'(DefaultFixedFont$, FontSize)+'df'+'lpi'(SPRows/Length)+'cpi'(SPCols/Width)
        if CPFont$ = "" and FontCols >= CPCols and FontRows >= CPRows \
            then CPFont$ = 'font'(DefaultFixedFont$, FontSize)+'df'+'lpi'(CPRows/Length)+'cpi'(CPCols/Width)
        if EPFont$ = "" and FontCols >= EPCols and FontRows >= EPRows \
            then EPFont$ = 'font'(DefaultFixedFont$, FontSize, "B")+'df'+'lpi'(EPRows/Length)+'cpi'(EPCols/Width)
    next x

    return 0
!
!
! EOF will be 1 if we are at the end of file
! Lockable will be 1 if we are the only accessor
! A report has finished spooling to disk if EOF is one and Lockable is 1
! Sets property ReportType  1 = Text with FF, 2 = Text without FF, 3 = GUI (must have FF)
! Return 0 if unsure, 1 if we've figured it out.
DetermineReportType:
    if EOF = 0 or Lockable = 0 then \
    {   FFs = pos($0C$ = Buffer$, 1, 0)
        LFs = pos($0A$ = Buffer$, 1, 0)

        if FileType = 3 and FFs then \
        {   FFs -= 1
            x = pos($0c$ = Buffer$, 1, 1)
            LFs = pos($0A$ = Buffer$(x + 1), 1, 0)
        }
        if FFs and FFs < 3 then return 0
        if FFs = 0 and LFs > 0 and LFs < 255 then return 0
        if FFs = 0 and LFs = 0 then return 0

    } else \
    {   FFs = pos($0C$ = Buffer$, 1, 0)
        LFs = pos($0A$ = Buffer$, 1, 0)
        if FileType = 3 and FFs then \
        {   FFs -= 1
            x = pos($0c$ = Buffer$, 1, 1)
            LFs = pos($0A$ = Buffer$(x + 1), 1, 0)
        }
    }
    ! Must figured out report type, either we've got enough data to do it, or
    ! the report is as big as it will get
    ! First Check, Does it contain GUI Mnemonics ? FONT, TEXT Short, TEXT Long, PICTURE, RECTANGLE, LINE
    if FileType = 3 and BannerLoaded = 0 \
        then StartLoc = pos($0C$ = Buffer$, 1, 1) + 1

    if StartLoc < 1 \
        then StartLoc = 1

    switch 1
        case mid(Buffer$, StartLoc, 2) = $0d0a$
            StartLoc +=2
            break
        case mid(Buffer$, StartLoc, 1) = $0d$
            StartLoc +=1
            break
        case mid(Buffer$, StartLoc, 1) = $0a$
            StartLoc +=1
            break
    esac

    ReportType = 3
        if pos($1B6D6E243141$ = Buffer$(StartLoc), 1) \
            then return ReportType
        if pos($1B6D78243141$ = Buffer$(StartLoc), 1) \
            then return ReportType
        if pos($1B6D6E243038$ = Buffer$(StartLoc), 1) \
            then return ReportType
        if pos($1B6D6E243041$ = Buffer$(StartLoc), 1) \
            then return ReportType
        if pos($1B6D6E243039$ = Buffer$(StartLoc), 1) \
            then return ReportType
        ! If it doesn't have one of the above, its doubtful its a GUI report
    ReportType = 1

    if FFs \
        then return ReportType
    ReportType = 2
    return ReportType
!
!
SetDefaultFixedFont:
    enter Value$

    if Value$ > "" then \
    {   if pos("," = Value$, 1, 0) > 0 \
            then read data from Value$,sep="," to DFont$, DFontSize, DFontAttrib$, DFontRot \
            else DFont$ = Value$
    }
    if DFontSize > 0 \
        then DFontSize = 0

    if DFont$ > "" then DefaultPage'DefaultFont$ = DFont$, DefaultFixedFont$ = DFont$
    if DFontSize <> 0 then DefaultPage'DefaultFontSize = DFontSize, DefaultFixedFontSize = DFontSize
    if DFontAttrib$ > "" then DefaultPage'DefaultFontAttributes$ = DFontAttrib$
    if DFontRot <> 0 then DefaultPage'DefaultFontRotation = DFontRot

    return 1
!
!
SetDefaultFixedFontSize:
    enter Value%
    DFontSize = Value%
    if DFontSize > 0 \
        then DFontSize *= -1
    if DFontSize <> 0 then DefaultPage'DefaultFontSize = DFontSize, DefaultFixedFontSize = DFontSize
    return 1
!
! Returns 0 on success anything else is error
OpenFile:
    wait 0
    enter (Value$)

    if Value$ = "" \
        then return 48

    LoadPointer = 0
    FetchMore = 1
    Buffer$ = ""
    FileType = 0
    open object (hfn, isz=1, err=OFOpenErr) Value$
    FileChan = lfo
    if ucs(mid(fib(FileChan), 19, 1)) <> "B" \
        then close(FileChan);
             return 17

    if and(dec($00$ + mid(fin(FileChan), 27, 2)), dec($0020$)) \
        then FileIsReadOnly = 1

    l = 0, h$ = ""
    while len(h$) < 8
        read record(FileChan, siz=1, err=*next) x$;
            h$ += x$;
            continue
        ! See if its spooled or not
        This'CheckLockable()
        if Lockable = 0 then \
            wait .1;
            continue
        ! I can lock it, so there is either nothing, or very little in it.
        break
    wend

    switch mid(h$, 1, 8)
        case "[Pvxind]"; FileType = 5; break
        case "[Pvxkey]"; FileType = 4; break
        case "[Pvxky2]"; FileType = 4; break
        case "[Pvxrpt]"; FileType = 3; break
        default; FileType = 2
    esac
    close(FileChan)

    if FileType > 3 \
        then open object (hfn, err=OFOpenErr) Value$ \
        else open object (hfn, isz=1, err=OFOpenErr) Value$
    FileChan = lfo
    FileName$ = pth(FileChan)
    return 0
!
OFOpenErr:
    RCounter++
    FileChan = 0
    if err = 0 and RCounter < 5 \
        then retry
    if err = 0 \
        then return 256
    return err
!
!
! lock needs an RTY=0 option to simplify this code
CheckLockable:
    if FileIsReadOnly then \
    {   Lockable = 1
        return 1
    }

    x = prm('WT')
    setparam 'WT'=0
    Lockable = 0
    lock(FileChan,err=*next);
         Lockable = 1;
         unlock(FileChan,err=*next)
    setparam 'WT' = x
    return Lockable
!
!
! Method: Page()
! Method: Page(PageNum%)
! Use this method to get access to methods and properties of the pages contained
! in the Sheets object
! Page = 0  is the default page object
GetPage:
    PageNum% = Page
    enter (PageNum%), err=*proceed
    if PageNum% < 0 or PageNum% > Sheets'Count \
        then return 0
    if PageNum% = 0 \
        then return DefaultPage \
        else return Sheets'Item(PageNum%)
!
!
! Method: Info(Item%, Name$, Value$)
! Returns Report info to Banner routine in Preview
GetInformation:
    enter (Item%), Name$, Value$
    if Item% <= 0 or Item% > Info'Items \
        then return 41
    return Info'GetItem(Item%, Name$,Value$)
!
!
OnCreate:
    This = _obj
    BITMAP = 1
    WINPRT = 2
    PDF = 3
    Software$ = msg("Tsoftware")
    FileName$ = "", FileName = 0, FileIsObject = 0, WaterMark = 0
    enter FileName, WaterMark, err=TryString
    FileIsObject = 1
    FileType = 1
    if FileName = 0 \
        then exit 41
    FileName$=FileName'_Class$
    x = ref(add FileName,err=*proceed)
    goto OCGotParams

    TryString:
    enter FileName$, WaterMark
    FileIsObject = 0
    result = This'OpenFile(FileName$)
    if result \
        then exit result

    OCGotParams:
    This = _obj

    Sheets = new("*obj/group","*viewer/page")
    Info = new("*viewer/list.name.value")
    DefaultPage = new("*viewer/page")

    ParametersSoftware = new("*viewer/parameters", "*viewer/software.prm")

    ParametersSoftware'AddVariable("Viewer","FontInfo","DefaultFixedFont","S","","","","Courier New")
    ParametersSoftware'AddVariable("Viewer","FontInfo","DefaultFixedFontSize","N","-72","72","","-12")

    ParametersSoftware'AddVariable("Viewer","MnemonicInfo","SPCols","N","0","255","","0")
    ParametersSoftware'AddVariable("Viewer","MnemonicInfo","SPRows","N","0","255","","0")
    ParametersSoftware'AddVariable("Viewer","MnemonicInfo","CPCols","N","0","255","","0")
    ParametersSoftware'AddVariable("Viewer","MnemonicInfo","CPRows","N","0","255","","0")
    ParametersSoftware'AddVariable("Viewer","MnemonicInfo","EPCols","N","0","255","","0")
    ParametersSoftware'AddVariable("Viewer","MnemonicInfo","EPRows","N","0","255","","0")

    ParametersSoftware'AddVariable("Viewer","TabInfo","TabSize","N","0","12","","4")

    ParametersSoftware'GetParameters("Viewer","FontInfo")
    ParametersSoftware'GetParameters("Viewer","MnemonicInfo")
    ParametersSoftware'GetParameters("Viewer","TabInfo")
    drop object ParametersSoftware


    Spooled = 0
    Spool = 1

    Collate = 1
    Colour = 1
    Range$ = "All"
    Copies = 1

    MinimumColumns          = 0
    MinimumRows             = 0
    PaginationAt            = 0

    DefaultPage'DefaultFont$ = DefaultFixedFont$
    DefaultPage'DefaultFontSize = DefaultFixedFontSize
    DefaultPage'DefaultFontAttributes$ = ""
    DefaultPage'DefaultFontRotation = 0

    DefaultPage'CurrentFont$ = DFont$
    DefaultPage'CurrentFontSize = DFontSize
    DefaultPage'CurrentFontAttributes$ = DFontAttrib$
    DefaultPage'CurrentFontRotation = DFontRot

    DefaultPage'LPI = 0
    DefaultPage'CPI = 0
    DefaultPage'ForegroundColour$ = 'F0'

    return
!
OnDelete:
    if FileIsObject = 1 \
        then x = ref(drop FileName,err=*proceed)
    if FileChan \
        then close(FileChan)

    if Sheets \
        then drop object Sheets
    if Info \
        then drop object Info
    return
!
!
GetPageData:
    enter (Value%), PageData$

    CurrentPage = This'Page(Value%)
    PageLoc$ = CurrentPage'Data$

    ! Retrieve the info for this page
    switch FileType
        case 1 ! Its an Object
            break

        case 2, 3 ! Binary File
            read data from PageLoc$,sep=":" to s, z
            read record(FileChan, ind = s, siz = z, err=*proceed) PageData$
            break

        case 4 ! Keyed
            break

        case 5 ! Indexed
            break
    esac

    ! Strip any '*C'
    if pos($1B2a43$ = PageData$, 1) \
        then Lockable = 1
    PageData$ = sub(PageData$, $1B2A43$, "")
    ! Turn any tabs into some spaces.
    PageData$ = sub(PageData$, $09$, dim(TabSize, " "))
    return 1
!
! Load the Banner Information up.
LoadBannerInfo:
    enter (Value$)

    if tcb(88) then IsWindX$="[wdx]"
    call IsWindX$+"*windx.utl;Get_Num","tcb(44)",timeoffset

    switch FileType
        case 3 ! Binary with Banner
            if pos($0d0a$ = Value$, 1) \
                then Value$ = sub(Value$,$0d$,$$)
            if pos($0d$ = Value$, 1) \
                then Value$ = sub(Value$,$0d$,$0a$)

            x = pos($0a$ = Value$, 1, 0)
            if x = 0 \
                then break

            dim x$[1 : x]
            read data from Value$,sep=$0a$ to x${all}

            gmtmask$ = stp(msg("DateMaskGMT"))
            if gmtmask$ = "DateMaskGMT:" \
                then gmtmask$ = ""

            localmask$ = stp(msg("DateMaskLocal"))
            if localmask$ = "DateMaskLocal:" \
                then localmask$ = ""


            for y = 1 to x
                n$ = "", v$ = ""
                read data from x$[y],sep="=" to n$, v$
                if n$ = "" \
                    then continue

                if v$ > "" then \
                { !
                    switch ucs(n$)
                        case "TITLE"
                            Title$ = v$
                            SkipEntry = 1
                            break

                        case "USER"
                            break

                        case "PRINTER"
                            Printer$ = v$

                        case "CREATED"
                            d$ = v$
                            n$ = "createdgmt"
                            v$ = dte(jul(1970,1,1)+num(d$,err=*next)/60/60/24,*:gmtmask$, err=*next)
                            n1$ = "created"
                            v1$ = dte(jul(1970,1,1)+(num(d$,err=*next) - timeoffset)/60/60/24,*:localmask$, err=*next)
                            break

                        case "PAPERSIZE"
                            DefaultPage'PaperSize = num(v$, err=*next)
                            v$ = DefaultPage'PaperName$
                            break

                        case "ORIENTATION"
                            DefaultPage'Orientation$ = v$
                            n = num(v$, err=*next);
                                DefaultPage'Orientation = n
                            v$ = msg("T" + lcs(DefaultPage'Orientation$))
                            break

                        case "SOURCE"
                            DefaultPage'PaperBin = num(v$, err=*next)
                            v$ = DefaultPage'PaperBinName$
                            break

                        case "QUALITY"
                            DPI = num(v$, err=*next)
                            break

                        case "DPI"
                            ScreenDPI = num(v$, err=*next)
                            break

                        case "COLOUR"
                            Colour = num(v$, err=*next)
                            break

                        case "COLOR"
                            Colour = num(v$, err=*next)
                            break

                        case "COLLATE"
                            if ucs(v$)="YES" then v$="1"
                            if ucs(v$)="NO" then v$="0"
                            Collate = num(v$, err=*next)
                            v$ = tbl(Collate = 1, msg("Tno"), msg("Tyes"))
                            break

                        case "SUPPRESSWATERMARKS"
                            if ucs(v$)="YES" then v$="1"
                            if ucs(v$)="NO" then v$="0"
                            SuppressWatermarks = num(v$, err=*next)
                            v$ = msg("Isuppressed")
                            break

                        case "COPIES"
                            Copies = num(v$, err=*next)
                            break

                        case "RANGE"
                            Range$ = v$
                            break

                        case "MARGIN","MARGINS"
                            m1 = -1, m2 = -1, m3 = -1, m4 = -1
                            s = pos(":" = v$, 1, 0)
                            if s = 0 \
                                then read data from v$,sep=":" to m1
                            if s = 1 \
                                then read data from v$,sep=":" to m1, m2
                            if s = 2 \
                                then read data from v$,sep=":" to m1, m2, m3
                            if s = 3 \
                                then read data from v$,sep=":" to m1, m2, m3, m4
                            DefaultPage'MarginLeft = m1
                            DefaultPage'MarginTop = m2
                            DefaultPage'MarginRight = m3
                            DefaultPage'MarginBottom = m4
                            v$ = str(DefaultPage'MarginLeft)+":"+str(DefaultPage'MarginTop)+":"+ \
                                 str(DefaultPage'MarginRight)+":"+str(DefaultPage'MarginBottom)
                            break

                        case "COLUMNS"
                            MinimumColumns = num(v$, err=*next)
                            break

                        case "ROWS"
                            MinimumRows = num(v$, err=*next)
                            break

                        case "PAGINATIONAT"
                            PaginationAt = num(v$, err=*next)
                            break

                        case "SCALETOFIT"
                            ScaleToFit = num(v$, err=*next)
                            v$ = tbl(ScaleToFit = 1, msg("Toff"), msg("Ton"))
                            break

                        case "FONT"
                            DefaultFixedFont$ = v$
                            if DefaultFixedFont$ > "" then \
                            {   if pos("," = DefaultFixedFont$, 1, 0) > 0 \
                                    then read data from DefaultFixedFont$,sep="," to DFont$, DFontSize, DFontAttrib$, DFontRot \
                                    else DFont$ = DefaultFixedFont$
                            }
                            if DFontSize > 0 \
                                then DFontSize = 0

                            if DFont$ > "" then DefaultPage'DefaultFont$ = DFont$, DefaultFixedFont$ = DFont$
                            if DFontSize <> 0 then DefaultPage'DefaultFontSize = DFontSize, DefaultFixedFontSize = DFontSize
                            if DFontAttrib$ > "" then DefaultPage'DefaultFontAttributes$ = DFontAttrib$
                            if DFontRot <> 0 then DefaultPage'DefaultFontRotation = DFontRot
                            break

                        case "FONTSIZE"
                            DFontSize = num(v$, err=*next)
                            if DFontSize > 0 \
                                then DFontSize *= -1
                            if DFontSize <> 0 then DefaultPage'DefaultFontSize = DFontSize, DefaultFixedFontSize = DFontSize
                            break

                        case "SPCOLS"
                            SPCols = num(v$, err=*next)
                            SKipEntry = 1
                            break

                        case "SPROWS"
                            SPRows = num(v$, err=*next)
                            SKipEntry = 1
                            break

                        case "CPCOLS"
                            CPCols = num(v$, err=*next)
                            SKipEntry = 1
                            break

                        case "CPROWS"
                            CPRows = num(v$, err=*next)
                            SKipEntry = 1
                            break

                        case "EPCOLS"
                            EPCols = num(v$, err=*next)
                            SKipEntry = 1
                            break

                        case "EPROWS"
                            EPRows = num(v$, err=*next)
                            SKipEntry = 1
                            break

                        case "SUPPRESSFIRSTBLANKPAGE"
                            if num(v$, err=*next) = 1 \
                                then SuppressFirstBlankPage = 1
                            if num(v$, err=*next) = 0 \
                                then SuppressFirstBlankPage = 0
                            SkipEntry = 1
                            break

                        case "SUPPRESSALLBLANKPAGES"
                            if num(v$, err=*next) = 1 \
                                then SuppressAllBlankPages = 1
                            if num(v$, err=*next) = 0 \
                                then SuppressAllBlankPages = 0
                            SkipEntry = 1
                            break

                        case "WATERMARKTEXT"
                            This'WaterMark'Text$ = v$
                            SkipEntry = 1
                            break

                        case "WATERMARKTEXTLOCATION"
                            if num(v$, err=*next) \
                                then This'WaterMark'TextLocation = num(v$)
                            SkipEntry = 1
                            break

                        case "WATERMARKTEXTFONT"
                            This'WaterMark'Font$ = v$
                            SkipEntry = 1
                            break

                        case "WATERMARKTEXTROTATION"
                            if num(v$, err=*next) \
                                then This'WaterMark'Rotation = num(v$)
                            SkipEntry = 1
                            break

                        case "WATERMARKIMAGE"
                            This'WaterMark'Image$ = v$
                            SkipEntry = 1
                            break

                        case "WATERMARKIMAGELOCATION"
                            if num(v$, err=*next) \
                                then This'WaterMark'ImageLocation = num(v$)
                            SkipEntry = 1
                            break

                    esac
                } else \
                { !
                    switch ucs(n$)
                        case "SCALETOFIT"
                            ScaleToFit = 1
                            v$ = tbl(ScaleToFit = 1, msg("Toff"), msg("Ton"))
                            break

                        case "SUPPRESSFIRSTBLANKPAGE"
                            SuppressFirstBlankPage = 1
                            SkipEntry = 1
                            break

                        case "SUPPRESSALLBLANKPAGES"
                            SuppressAllBlankPages = 1
                            SkipEntry = 1
                            break

                        case "SUPPRESSWATERMARKS"
                            v$ = msg("Isuppressed")
                            SuppressWatermarks = 1
                            break

                    esac
                }

                ! Convert Info to local language
                if n$ > "" then \
                {   m$ = stp(msg("I" + lcs(n$)))
                    if m$ = "I" + lcs(n$) + ":" \
                        then m$ = ""
                }

                if n1$ > "" then \
                {   m1$ = stp(msg("I" + lcs(n1$)))
                    if m1$ = "I" +lcs(n1$) + ":" \
                        then n1$ = ""
                }

                if not(SkipEntry) then \
                {   if m1$ > "" \
                        then Info'SetItem(m1$, v1$)
                    if m$ > ""  \
                        then Info'SetItem(m$, v$)
                }
                SkipEntry = 0
                n$ = "", n1$ = "", m$ = "", m1$ = ""
            next y

            break
    esac

    BannerLoaded = 1
    return 1
!
!
! Method: GetPageAttributes(Page, PaperSize, Orientation, PaperBin, DefaultFont$, \
!                           DefaultFontSize, DefaultFontAttributes$, DefaultFontRotation, \
!                           CurrentFont$, CurrentFontSize, CurrentFontAttributes$, CurrentFontRotation, \
!                           LPI, CPI, ForegroundColour$, BackgroundColour$, Mode$, \
!                           Pen$, Fill$, UnderLine, ReverseVideo, \
!                           MarginLeft, MarginTop, MarginRight, MarginBottom)
! Retrieves a page's attributes
GetPageAttributes:
    enter (Value), PSize, POrient, PBin, DFont$, DFontSize, DFontAttrib$, DFontRot, \
                    CFont$, CFontSize, CFontAttrib$, CFontRot, LPI, CPI, \
                    FColour$, BColour$, CMode$, Pen$, Fill$, UnderLine, ReverseVideo, \
                    MLeft, MTop, MRight, MBottom, SPCPMode, PlusS, err=*proceed

    CurrentPage = This'Page(Value)
    PSize = CurrentPage'PaperSize, \
    POrient = CurrentPage'Orientation, \
    PBin = CurrentPage'PaperBin

    DFont$ = CurrentPage'DefaultFont$, \
    DFontSize = CurrentPage'DefaultFontSize, \
    DFontAttrib$ = CurrentPage'DefaultFontAttributes$, \
    DFontRot = CurrentPage'DefaultFontRotation

    CFont$ = CurrentPage'CurrentFont$, \
    CFontSize = CurrentPage'CurrentFontSize, \
    CFontAttrib$ = CurrentPage'CurrentFontAttributes$, \
    CFontRot = CurrentPage'CurrentFontRotation

    LPI = CurrentPage'LPI, \
    CPI = CurrentPage'CPI

    FColour$ = CurrentPage'ForegroundColour$, \
    BColour$ = CurrentPage'BackgroundColour$, \
    CMode$ = CurrentPage'Mode$

    Pen$ = CurrentPage'Pen$, \
    Fill$ = CurrentPage'Fill$

    UnderLine = CurrentPage'UnderLine
    ReverseVideo = CurrentPage'ReverseVideo

    MLeft = CurrentPage'MarginLeft, \
    MTop = CurrentPage'MarginTop, \
    MRight = CurrentPage'MarginRight, \
    MBottom = CurrentPage'MarginBottom

    SPCPMode = CurrentPage'SPCPMode
    PlusS = CurrentPage'PlusS

    return 0
!
!
! Method: SetPageAttributes(Page, PaperSize, Orientation, PaperBin, DefaultFont$, \
!                           DefaultFontSize, DefaultFontAttributes$, DefaultFontRotation, \
!                           CurrentFont$, CurrentFontSize, CurrentFontAttributes$, CurrentFontRotation, \
!                           LPI, CPI, ForegroundColour$, BackgroundColour$, Mode$, \
!                           Pen$, Fill$, UnderLine, ReverseVideo, \
!                           MarginLeft, MarginTop, MarginRight, MarginBottom, SPCPMode, PlusS)
! Sets all the page's attributes
SetPageAttributes:
    enter (Value), PSize, POrient, PBin, DFont$, DFontSize, DFontAttrib$, DFontRot, \
                    CFont$, CFontSize, CFontAttrib$, CFontRot, LPI, CPI, \
                    FColour$, BColour$, CMode$, Pen$, Fill$, UnderLine, ReverseVideo, \
                    MLeft, MTop, MRight, MBottom, SPCPMode, PlusS, err=*proceed

    if HasSPCP and Value = 0 \
        then SPCPMode = 1

    CurrentPage = This'Page(Value)
    CurrentPage'PaperSize = PSize, \
    CurrentPage'Orientation = POrient, \
    CurrentPage'PaperBin = PBin

    CurrentPage'DefaultFont$ = DFont$, \
    CurrentPage'DefaultFontSize = DFontSize, \
    CurrentPage'DefaultFontAttributes$ = DFontAttrib$, \
    CurrentPage'DefaultFontRotation = DFontRot

    CurrentPage'CurrentFont$ = CFont$, \
    CurrentPage'CurrentFontSize = CFontSize, \
    CurrentPage'CurrentFontAttributes$ = CFontAttrib$, \
    CurrentPage'CurrentFontRotation = CFontRot

    CurrentPage'LPI = LPI, \
    CurrentPage'CPI = CPI

    CurrentPage'ForegroundColour$ = FColour$, \
    CurrentPage'BackgroundColour$ = BColour$, \
    CurrentPage'Mode$ = CMode$

    CurrentPage'Pen$ = Pen$, \
    CurrentPage'Fill$ = Fill$

    CurrentPage'UnderLine = UnderLine
    CurrentPage'ReverseVideo = ReverseVideo

    CurrentPage'MarginLeft = MLeft, \
    CurrentPage'MarginRight = MRight, \
    CurrentPage'MarginTop = MTop, \
    CurrentPage'MarginBottom = MBottom

    CurrentPage'SPCPMode = SPCPMode
    CurrentPage'PlusS = PlusS
    return 0
!
!
! Method: OutputPageAttributes(Page%, Channel%)
! Setups the page with the correct attributes so everything fits properly
! Works by getting the attributes that the previous page ended with
! and outputting them as how the page requested needs to start.
OutputPageAttributes:
    enter (Value%),(ChannelNum%)
    ! Retrieve the previous pages ending state

    This'GetPageAttributes(Value% - 1, PSize, POrient, PBin, DFont$, DFontSize, \
                    DFontAttrib$, DFontRot, CFont$, CFontSize, CFontAttrib$, CFontRot, LPI, CPI, \
                    FColour$, BColour$, CMode$, Pen$, Fill$, UnderLine, ReverseVideo, \
                    MLeft, MTop, MRight, MBottom, SPCPMode, PlusS)

    ! Output that ending state
    if This'IsGUIDevice(ChannelNum%) then \
    {
        if DFontSize <> 0 \
            then print (ChannelNum%, err=*proceed) 'font'(DFont$, DFontSize, DFontAttrib$, DFontRot),'df', \
            else print (ChannelNum%, err=*proceed) 'font'(DFont$),'df',

        if LPI \
            then print (ChannelNum%, err=*proceed) 'lpi'(LPI),
        if CPI \
            then print (ChannelNum%, err=*proceed) 'cpi'(CPI),

        if Value% = 1 and HasSPCP \
            then print (ChannelNum%, err=*proceed) SPFont$,

        if HasSPCP and SPCPMode = 1 \
            then print (ChannelNum%, err=*proceed) SPFont$,
        if HasSPCP and SPCPMode = 2 \
            then print (ChannelNum%, err=*proceed) CPFont$,
        if HasSPCP and SPCPMode = 3 \
            then print (ChannelNum%, err=*proceed) EPFont$,

        if CFontSize <> 0 \
            then print (ChannelNum%, err=*proceed) 'font'(CFont$, CFontSize, CFontAttrib$, CFontRot), \
            else print (ChannelNum%, err=*proceed) 'font'(CFont$),

        if Pen$ > "" \
            then read data from pen$,sep="," to s,w,c;
                 print (ChannelNum%, err=*proceed) 'pen'(s,w,c),
        if Fill$ > "" \
            then read data from fill$,sep="," to p,c;
                 print (ChannelNum%, err=*proceed) 'fill'(p,c),
        if PlusS \
            then print (ChannelNum%, err=*proceed) '+S',
    }
    if not(This'IsGUIDevice(ChannelNum%)) then \
    {
        if HasSPCP and SPCPMode = 1 \
            then print (ChannelNum%, err=*proceed) 'SP',
        if HasSPCP and SPCPMode = 2 \
            then print (ChannelNum%, err=*proceed) 'CP',
        if HasSPCP and SPCPMode = 3 \
            then print (ChannelNum%, err=*proceed) 'EP',
    }
    if FColour$ > "" \
        then print (ChannelNum%, err=*proceed) FColour$,
    if BColour$ > "" \
        then print (ChannelNum%, err=*proceed) BColour$,
    if Underline \
        then print (ChannelNum%, err=*proceed) 'bu',
    if ReverseVideo \
        then print (ChannelNum%, err=*proceed) 'br',
    if CMode$ > "" \
        then print (ChannelNum%, err=*proceed) 'mode'(CMode$),


    return 0
!
!
! Method: ScanForAttributes(PageData$, PaperSize, Orientation, PaperBin, DefaultFont$, \
!                           DefaultFontSize, DefaultFontAttributes$, DefaultFontRotation, \
!                           CurrentFont$, CurrentFontSize, CurrentFontAttributes$, CurrentFontRotation, \
!                           LPI, CPI, ForegroundColour$, BackgroundColour$, Mode$, \
!                           Pen$, Fill$, UnderLine, ReverseVideo, \
!                           MarginLeft, MarginTop, MarginRight, MarginBottom, SPCPMode, PlusS)
! Pass in the previous's page attributes, the current page's PageData will be scanned, and an updated
! list of attributes will be returned
ScanForAttributes:
    enter (Value$), PSize, POrient, PBin, DFont$, DFontSize, DFontAttrib$, DFontRot, \
                    CFont$, CFontSize, CFontAttrib$, CFontRot, LPI, CPI, \
                    FColour$, BColour$, CMode$, Pen$, Fill$, UnderLine, ReverseVideo, \
                    MLeft, MTop, MRight, MBottom, SPCPMode, PlusS, err=*proceed

    if (ReportType = 1 or ReportType = 2) and DFontSize = 0 \
        then if POrient = 0 \
                 then DFontSize = PortraitFontSize \
                 else DFontSize = LandscapeFontSize

    e = pos(esc = Value$, 1, 0)
    if e = 0 \
        then return 0

    for x = 1 to e
        y = pos(esc = Value$, 1)
        if y = 0 \
            then break
        Value$ = Value$(y)
        if len(Value$) < 3 \
            then break

        mcode$ = ""
        switch mid(Value$,1, 3)
            ! Codes we can ignore
            case esc+"JS"; break
            case esc+"JC"; break
            case esc+"JD"; break
            case esc+"JN"; break
            case esc+"JL"; break
            case esc+"JR"; break
            case esc+"*C"; break
            case esc+"+S"; PlusS = 1; break
            case esc+"-S"; PlusS = 0; break

            case esc+"SP"; SPCPMode = 1, HasSPCP = 1; break
            case esc+"CP"; SPCPMode = 2; HasSPCP = 1; break
            case esc+"EP"; SPCPMode = 3; HasSPCP = 1; break

            case esc+"BU"; Underline = 1; break
            case esc+"EU"; Underline = 0; break
            case esc+"BR"; ReverseVideo = 1; break
            case esc+"ER"; ReverseVideo = 0; break

            case esc+"DF"
                DFont$ = CFont$
                DFontSize = CFontSize
                DFontAttrib$ = CFontAttrib$
                DFontRot = CFontRot
                LPI = 0
                CPI = 0
                break
            case esc+"PM"; POrient = 0; break
            case esc+"LM"; POrient = 1; break
            case esc+"mn" ! Complex mnemonic
                fcode$ = mid(Value$, 4, 1) ! should always be "$" when in markup
                mcode$ = mid(Value$, 5, 2)
                mlength = dec($00$ + ath(mid(Value$, 7, 2)))
                if 9 + mlength * 2 > len(Value$) then \
                {   mcode$ = ""
                    break
                }
                mvalue$ = ath(mid(Value$, 9, mlength * 2))
                Value$ = Value$(6 + mlength * 2)
                break
            case esc+"mx" ! Complex mnemonic
                fcode$ = mid(Value$, 4, 1) ! should always be "$" when in markup
                mcode$ = mid(Value$, 5, 2)
                mlength = dec($00$ + ath(mid(Value$, 7, 8)))
                if 15 + mlength * 2 > len(Value$) then \
                {   mcode$ = ""
                    break
                }
                mvalue$ = ath(mid(Value$, 5, mlength * 2))
                Value$ = Value$(12 + mlength * 2)
                break
            ! Foreground Colours
            case esc+"F0"
            case esc+"F1"
            case esc+"F2"
            case esc+"F3"
            case esc+"F4"
            case esc+"F5"
            case esc+"F6"
            case esc+"F7"
            case esc+"F8"
            case esc+"F9"
            case esc+"F:"
            case esc+"F;"
            case esc+"F<"
            case esc+"F="
            case esc+"F>"
            case esc+"F?"
                FColour$ = mid(Value$,1,3)
                break
            ! Background Colours
            case esc+"B0"
            case esc+"B1"
            case esc+"B2"
            case esc+"B3"
            case esc+"B4"
            case esc+"B5"
            case esc+"B6"
            case esc+"B7"
            case esc+"B8"
            case esc+"B9"
            case esc+"B:"
            case esc+"B;"
            case esc+"B<"
            case esc+"B="
            case esc+"B>"
            case esc+"B?"
                BColour$ = mid(Value$,1,3)
                break

            default
                break
        esac
        Value$ = Value$(4)

        if mcode$ = "" \
            then continue
        switch mcode$
            ! Codes we can ignore
            case "1A" ! TEXT()
            case "09" ! LINE()
            case "0A" ! RECTANGLE()
            case "08" ! PICTURE()
                break

            case "12" ! FONT()
                CFont$ = DFont$
                CFontSize = DFontSize
                CFontAttrib$ = DFontAttrib$
                CFontRot = DFontRot
                read data from CFont$,sep="," to f$,fs,fa$,fa
                p = 1, ml = len(mvalue$)
                while p < ml
                    i = asc(mvalue$(p++, 1))
                    f$ = mvalue$(p, i), p += i
                    if pos("," = f$, 1) \
                        then read data from f$,sep="," to f$,fs,fa$,fa
                    if p > ml then break
                    fs = dec(mvalue$(p, 2)) / 100, p += 2
                    if p > ml then break
                    i = asc(mvalue$(p++, 1))
                    fa$ = mvalue$(p, i), p += i
                    if p > ml then break
                    fa = dec(mvalue$(p, 2))
                    p += 2
                wend
                if f$ > "" then CFont$ = f$
                if fs < 0 then CFontSize = fs
                if fs > 0 then CFontSize = prc(DFontSize * fs, 2)
                if fa$ > "" then CFontAttrib$ = fa$
                if fa > 0 then CFontRot = fa
                break
            case "20" ! LPI()
                lpi = dec($00$ + mvalue$) / 100
                break
            case "21" ! CPI()
                cpi = dec($00$ + mvalue$) / 100
                break
            case "2B" ! 'Colour'()
                FColour$ = 'color'(mid(mvalue$,2))
                break
            case "2C" ! '_Colour'()
                BColour$ = '_color'(mid(mvalue$,2))
                break
            case "10" ! PEN()
                Pen$ = str(dec(mid(mvalue$,1,2)))+","+str(dec(mid(mvalue$,3,2)))+","+str(dec(mid(mvalue$,5,2)))
                break
            case "11" ! FILL()
                Fill$ = str(dec(mid(mvalue$,1,2)))+","+str(dec(mid(mvalue$,3,2)))
                break
            default
                break
        esac

    next x

    return 0
!
!
!    local SearchText$, SearchTable, SearchOccurs, SearchPage, SearchCase
!    local SearchedPage, SearchLastOccur
!    property Search                        ! 0 = Inactive, 1 = Active
!    function Search(Text$, CaseSensitive, Direction, Page, XLocation, YLocation)   SearchForText
!
!    Returns 0=Not Found, >0 = Found and Occurence in page
!    Direction, 0 = Forward, <>0 =  Backward
!    X and Y locations are rough locations of text as a column and row
!    Search property must be set to active.
!    Page if given is the starting page, and will be returned as the resulting page
SearchForText:

    enter (FindText$), (FindCase), (FindDirection), FindPage, FoundX, FoundY

    if not(Search) \
        then return 0

    FindText$ = stp(FindText$, 2)
    if len(FindText$) = 0 \
        then return 0

    TotalPages = This'Pages

    if FindPage <= 0 or FindPage > TotalPages then \
    {   if FindDirection then \
        {   FindPage = TotalPages
            SearchLastOccur = -1
        } else \
        {   FindPage = 1
            SearchLastOccur = 0
        }
        SearchedPage = 0
    }

    if FindText$ <> SearchText$ then \
        SearchedPage = 0

    SFTTryPage:

    if SearchedPage = 0 or \
       FindPage <> SearchedPage then \
    {   if FindDirection \
            then SearchLastOccur = -1 \
            else SearchLastOccur = 0
    }

    if FindPage < 1 \
    {   FindPage = 0
        return 0
    }

    if FindPage > TotalPages \
    {   FindPage = 0
        return 0
    }

    if SearchedPage = 0 or SearchedPage <> FindPage then \
    {   This'SearchInit()
        SearchText$ = FindText$
        SearchPage = FindPage
        SearchCase = FindCase
        SearchOccurs = This'SearchPage(SearchPage, SearchText$, SearchCase)
    }

    if SearchOccurs = 0 then \
    {   if FindDirection then \
        {   FindPage--
            wait 0
            goto SFTTryPage
        } else \
        {   FindPage++
            wait 0
            goto SFTTryPage
        }
    }

    ! There are occurrences in this page

    if FindDirection then \
    {   if SearchLastOccur = -1 then \
        {   ThisOccur = SearchOccurs
        } else \
        {   ThisOccur = SearchLastOccur - 1

            if SearchedPage = 1 and ThisOccur < 1 then \
            {   ThisOccur = 1
                SearchLastOccur = 1
            }

        }

        if ThisOccur < 1 then \
        {   FindPage--
            wait 0
            goto SFTTryPage
        }
    } else \
    {   if SearchLastOccur <= 0 then \
        {   ThisOccur = 1
        } else \
        {   if SearchedPage <= TotalPages then \
            {   if SearchedPage = TotalPages and SearchLastOccur + 1 > SearchOccurs \
                    then ThisOccur = SearchLastOccur \
                    else ThisOccur = SearchLastOccur + 1
            } else \
            {   ThisOccur = SearchLastOccur
            }
        }

        if ThisOccur > SearchOccurs then \
        {   FindPage++
            wait 0
            goto SFTTryPage
        }
    }

    FoundX = SearchTable[ThisOccur, 6] / 100
    FoundY = SearchTable[ThisOccur, 7] / 100

    if ThisOccur = SearchLastOccur \
        then return 0

    SearchLastOccur = ThisOccur

    return SearchLastOccur
!
!
! SearchTable [ Occurrence, 1:9 ]
! 1 - Byte Offset to start of text or mnemonic
! 2 - Length of Match, (Text or mnemonic)
! 3 - Plain Text = 0, GUI = 1
! 4 - XLocation
! 5 - YLocation
! 6 - XPercent - Location as a percentage across the page
! 7 - YPercent - Location as a percentage across the page
! 8 - Reverse Video On/Off = 1/0 at replacement point
! 9 - Underline On/Off =1/0 at replacement point
!
! SearchTable$ [ Occurence, 1:4 ]
! 1 - Text being replaced or mnemonic 'TEXT'() being replaced
!   - This will include the highlight colour code embedded in
! 2 - Foreground Colour at replacement point
! 3 - Background Colour at replacement point
! 4 - Mode at replacement point
!
SearchInit:
    SearchedPage = 0
    SearchCase = 0
    SearchOccurs = 0
    SearchPage = 0
    dim SearchTable[50, 1:9], SearchTable$[50, 1:1]
    return 0
!
!
SearchReset:
    SearchedPage = 0
    return 0
!
!
SearchPage:
    enter (ThisPage), (Text$), (MatchCase)
    This'GetPageData(ThisPage, PageData$)
    This'GetPageAttributes(ThisPage - 1, PSize, POrient, PBin, DFont$, DFontSize, \
                    DFontAttrib$, DFontRot, CFont$, CFontSize, CFontAttrib$, CFontRot, LPI, CPI, \
                    FColour$, BColour$, CMode$, Pen$, Fill$, UnderLine, ReverseVideo, MLeft, MTop, MRight, MBottom, SPCPMode, PlusS)

    PageObj = This'Page(ThisPage)
    if not(pos("WIN"=ucs(SYS),1)) and tcb(88) then IsWindX$ = "[WDX]"
    FontFunc = new(IsWindX$ + "*viewer/fontfunctions")

    if ScreenDPI \
        then FontFunc'DPI = ScreenDPI

    width = PageObj'PaperWidth
    length = PageObj'PaperLength
    margins$ = str(MLeft) + ":" + str(MTop) + ":" + str(MRight) + ":" + str(MBottom)

    bitmap = FontFunc'CreatePageAsBitmap(width, length, margins$)
    if DFontSize <> 0 \
        then FontFunc'PrintPageAsBitmap(bitmap, 'font'(DFont$, DFontSize, DFontAttrib$, DFontRot)+'df') \
        else FontFunc'PrintPageAsBitmap(bitmap, 'font'(DFont$)+'df')

    if LPI \
        then FontFunc'PrintPageAsBitmap(bitmap, 'lpi'(LPI))
    if CPI \
        then FontFunc'PrintPageAsBitmap(bitmap, 'cpi'(CPI))

    if CFontSize <> 0 \
        then FontFunc'PrintPageAsBitmap(bitmap, 'font'(CFont$, CFontSize, CFontAttrib$, CFontRot)) \
        else FontFunc'PrintPageAsBitmap(bitmap, 'font'(CFont$))

    if HasSPCP and SPCPMode <= 1 \
        then FontFunc'PrintPageAsBitmap(bitmap, SPFont$)
    if HasSPCP and SPCPMode = 2 \
        then FontFunc'PrintPageAsBitmap(bitmap, CPFont$)
    if HasSPCP and SPCPMode = 3 \
        then FontFunc'PrintPageAsBitmap(bitmap, EPFont$)

    result$ = FontFunc'GetMaximums$(bitmap)
    read data from result$,sep=":" to MaxColumns, MaxRows, MaxColGus, MaxRowGus, ColGu, RowGu

    HighlightColour$ = '_blue'+'white'

    TextLength = len(Text$)
    PageLength = len(PageData$)

    Occurs = 0, Found = 0
    Row = 0
    LineOfText$ = ""
    LineOfTextChars = 0
    dim LineOfText[1 : 1024] ! This is for the offset locations
    dim LineOfText$[1 : 1024] ! This is for the mnemonics
    ! For each character in the markup
    for x = 1 to PageLength
        offset = x
        ! If this is true, we've found plain text.
        if PageData$(x, 1)<>esc then \
        {
            if PageData$(x, 1) = $0A$ then \
            {
                if LineOfText$ > "" then \
                {
                    if MatchCase \
                        then OccursInLine = pos(Text$ = LineOfText$, 1, 0) \
                        else OccursInLine = pos(ucs(Text$) = ucs(LineOfText$), 1, 0)

                    if OccursInLine then \
                    {   for t = 1 to OccursInLine
                            if MatchCase \
                                then y = pos(Text$ = LineOfText$, 1, t) \
                                else y = pos(ucs(Text$) = ucs(LineOfText$), 1, t)

                            Occurs++
                            if (Occurs) | 50 = 0 then \
                            {   redim SearchTable[ Occurs + 50, 1:9 ]
                                redim SearchTable$[ Occurs + 50, 1:1 ]
                            }

                            SearchTable[Occurs, 1] = LineOfText[y]
                            if y + Textlength > len(LineOfText$) \
                                then SearchTable[Occurs, 2] = TextLength \
                                else SearchTable[Occurs, 2] = LineOfText[y + Textlength] - LineOfText[y]
                            SearchTable[Occurs, 3] = 0
                            SearchTable[Occurs, 4] = y - 1
                            SearchTable[Occurs, 5] = Row
                            SearchTable[Occurs, 6] = (y - 1 + (TextLength / 2)) / MaxColumns * 100
                            SearchTable[Occurs, 7] = Row / MaxRows * 100
                            SearchTable[Occurs, 8] = 0
                            SearchTable[Occurs, 9] = 0

                            s$ = ""
                            e$ = ""
                            read data from LineOfText$[y],sep="," to ReverseVideo, UnderLine, FColour$, BColour$
                            if ReverseVideo then \
                            {   s$ += 'ER'
                                e$ += 'BR'
                            }
                            if Underline then \
                            {   s$ += 'EU'
                                e$ += 'BU'
                            }
                            s$ += HighlightColour$

                            if FColour$ = "" \
                                then e$ += 'F0' \
                                else e$ += 'colour'(FColour$)

                            if BColour$ = "" \
                                then e$ += 'B7' \
                                else e$ += '_colour'(BColour$)

                            SearchTable$[Occurs, 1] = s$ + PageData$(SearchTable[Occurs, 1], SearchTable[Occurs, 2]) + e$

                        next t
                    }
                }

                Row++
                LineOfText$ = ""
                LineOfTextChars = 0
                dim LineOfText[1 : 1024] ! Should never be more than 255 chars, but lets play it safe
                dim LineOfText$[1 : 1024] ! Should never be more than 255 chars, but lets play it safe
                continue
            }

            z = pos($1B0A$ : PageData$(x), 1)
            if z = 0 \
                then z = PageLength - x + 1 \
                else z = z - 1
            TextData$ = PageData$(x, z)
            LineOfText$ += TextData$
            for t = 1 to len(TextData$)
                LineOfText[++LineOfTextChars] = x + t - 1
                LineOfText$[LineofTextChars] = str(ReverseVideo)+","+str(UnderLine)+","+FColour$+","+BColour$
            next t

            x = x + z - 1
            continue
        }

        ! We've found a mnemonic
        mcode$ = "", mnem = 0
        switch mid(PageData$,x, 3)
            case esc+"JS"; break
            case esc+"JC"; break
            case esc+"JD"; break
            case esc+"JN"; break
            case esc+"JL"; break
            case esc+"JR"; break
            case esc+"+S"; PlusS = 1; break
            case esc+"-S"; PlusS = 0; break

            case esc+"CP"
                FontFunc'PrintPageAsBitmap(bitmap, CPFont$)
                result$ = FontFunc'GetMaximums$(bitmap)
                read data from result$,sep=":" to MaxColumns, MaxRows, MaxColGus, MaxRowGus, ColGu, RowGu
                break
            case esc+"EP"
                FontFunc'PrintPageAsBitmap(bitmap, EPFont$)
                result$ = FontFunc'GetMaximums$(bitmap)
                read data from result$,sep=":" to MaxColumns, MaxRows, MaxColGus, MaxRowGus, ColGu, RowGu
                break
            case esc+"SP"
                FontFunc'PrintPageAsBitmap(bitmap, SPFont$)
                result$ = FontFunc'GetMaximums$(bitmap)
                read data from result$,sep=":" to MaxColumns, MaxRows, MaxColGus, MaxRowGus, ColGu, RowGu
                break

            case esc+"BU"; Underline = 1; break
            case esc+"EU"; Underline = 0; break
            case esc+"BR"; ReverseVideo = 1; break
            case esc+"ER"; ReverseVideo = 0; break
            ! Foreground Colours
            case esc+"F0"
            case esc+"F1"
            case esc+"F2"
            case esc+"F3"
            case esc+"F4"
            case esc+"F5"
            case esc+"F6"
            case esc+"F7"
            case esc+"F8"
            case esc+"F9"
            case esc+"F:"
            case esc+"F;"
            case esc+"F<"
            case esc+"F="
            case esc+"F>"
            case esc+"F?"
                FColour$ = PageData$(x, 3)
                break
            ! Background Colours
            case esc+"B0"
            case esc+"B1"
            case esc+"B2"
            case esc+"B3"
            case esc+"B4"
            case esc+"B5"
            case esc+"B6"
            case esc+"B7"
            case esc+"B8"
            case esc+"B9"
            case esc+"B:"
            case esc+"B;"
            case esc+"B<"
            case esc+"B="
            case esc+"B>"
            case esc+"B?"
                BColour$ = PageData$(x, 3)
                break
            case esc+"DF"
                DFont$ = CFont$
                DFontSize = CFontSize
                DFontAttrib$ = CFontAttrib$
                DFontRot = CFontRot
                LPI = 0
                CPI = 0
                FontFunc'PrintPageAsBitmap(bitmap, 'DF')
                result$ = FontFunc'GetMaximums$(bitmap)
                read data from result$,sep=":" to MaxColumns, MaxRows, MaxColGus, MaxRowGus, ColGu, RowGu
                break

            case esc+"mn" ! Complex mnemonic
                mnem = 1
                mnem$ = esc + "mn"
                offset = x
                fcode$ = PageData$(x + 3, 1) ! should always be "$" when in markup
                mcode$ = PageData$(x + 4, 2)
                mlength = dec($00$ + ath(PageData$(x + 6, 2)))
                mvalue$ = ath(PageData$(x + 8, mlength * 2))
                x = x + 5 + mlength * 2
                break
            case esc+"mx" ! Complex mnemonic
                mnem$ = esc + "mx"
                offset = x
                mnem = 2
                fcode$ = PageData$(x + 3, 1) ! should always be "$" when in markup
                mcode$ = PageData$(x + 4, 2)
                mlength = dec($00$ + ath(PageData$(x + 6, 8)))
                mvalue$ = ath(PageData$(x + 14, mlength * 2))
                x = x + 11 + mlength * 2
                break
        esac

        x += 2
        switch mcode$
            case "1A" ! TEXT()
                tmlength = x - offset + 1

                x1 = dec(mvalue$(1,2))
                y1 = dec(mvalue$(3,2))
                x2 = dec(mvalue$(5,2))
                y2 = dec(mvalue$(7,2))

                switch mnem
                    case 1
                        ttlen = asc(mvalue$(9,1))
                        mtext$ = mvalue$(10,ttlen)

                        if 10 + ttlen < mlength then \
                        {   moptlen = asc(mvalue$(10 + ttlen, 1))
                            mopt$ = mvalue$(10 + ttlen + 1, moptlen)
                        }
                        break
                    case 2
                        ttlen = asc(mvalue$(9,4))
                        mtext$ = mvalue$(13,ttlen)

                        if 13 + ttlen < mlength then \
                        {   moptlen = asc(mvalue$(13 + ttlen, 1))
                            mopt$ = mvalue$(13 + ttlen + 1, moptlen)
                        }
                        break
                esac

                if MatchCase \
                    then y = pos(Text$ = mtext$, 1) \
                    else y = pos(ucs(Text$) = ucs(mtext$), 1)
                if y = 0 \
                    then break

                Occurs++
                if (Occurs) | 50 = 0 then \
                {   redim SearchTable[ Occurs + 50, 1:9 ]
                    redim SearchTable$[ Occurs + 50, 1:1 ]
                }

                SearchTable[Occurs, 1] = offset
                SearchTable[Occurs, 2] = tmlength
                SearchTable[Occurs, 3] = 1
                SearchTable[Occurs, 4] = x1
                SearchTable[Occurs, 5] = y1
                SearchTable[Occurs, 6] = x1 / MaxColGus * 100
                SearchTable[Occurs, 7] = y1 / MaxRowGus * 100
                SearchTable[Occurs, 8] = 0
                SearchTable[Occurs, 9] = 0

                s$ = ""
                e$ = ""
                if ReverseVideo then \
                {   s$ += 'ER'
                    e$ += 'BR'
                }
                if Underline then \
                {   s$ += 'EU'
                    e$ += 'BU'
                }
                s$ += HighlightColour$
                e$ += FColour$+BColour$

                if mopt$ = "" then \
                {   if pos("&" = ucs(CFontAttrib$), 1) \
                        then mopt$ += "&"
                    if pos("F" = ucs(CFontAttrib$), 1) \
                        then mopt$ += "F"
                    if pos("S" = ucs(CFontAttrib$), 1) \
                        then mopt$ += "S"
                    if pos("R" = ucs(CFontAttrib$), 1) \
                        then mopt$ += "R"
                    if pos("C" = ucs(CFontAttrib$), 1) \
                        then mopt$ += "C"
                    if pos("W" = ucs(CFontAttrib$), 1) \
                        then mopt$ += "W"
                    if pos("#" = ucs(CFontAttrib$), 1) \
                        then mopt$ += "#"
                    if pos("N" = ucs(CFontAttrib$), 1) \
                        then mopt$ += "N"
                }
                mopt$ += "S"

                SearchTable$[Occurs, 1] = s$ + 'text'(x1, y1, x2, y2, mtext$, mopt$) + e$
                break

            case "2B" ! 'Colour'()
                FColour$ = 'Color'(mvalue$(2))
                break
            case "2C" ! '_Colour'()
                BColour$ = '_color'(mvalue$(2))
                break

            case "12" ! FONT()
                CFont$ = DFont$
                CFontSize = DFontSize
                CFontAttrib$ = DFontAttrib$
                CFontRot = DFontRot
                read data from CFont$,sep="," to f$,fs,fa$,fa
                p = 1, ml = len(mvalue$)
                while p < ml
                    i = asc(mvalue$(p++, 1))
                    f$ = mvalue$(p, i), p += i
                    if pos("," = f$, 1) \
                        then read data from f$,sep="," to f$,fs,fa$,fa
                    if p > ml then break
                    fs = dec(mvalue$(p, 2)) / 100, p += 2
                    if p > ml then break
                    i = asc(mvalue$(p++, 1))
                    fa$ = mvalue$(p, i), p += i
                    if p > ml then break
                    fa = dec(mvalue$(p, 2))
                    p += 2
                wend
                if f$ > "" then CFont$ = f$
                if fs < 0 then CFontSize = fs
                if fs > 0 then CFontSize = prc(DFontSize * fs, 2)
                if fa$ > "" then CFontAttrib$ = fa$
                if fa > 0 then CFontRot = fa
                FontFunc'PrintPageAsBitmap(bitmap, 'font'(CFont$,CFontSize,CFontAttrib$))
                break
            case "20" ! LPI()
                lpi = dec($00$ + mvalue$) / 100
                FontFunc'PrintPageAsBitmap(bitmap, 'lpi'(lpi))
                result$ = FontFunc'GetMaximums$(bitmap)
                read data from result$,sep=":" to MaxColumns, MaxRows, MaxColGus, MaxRowGus, ColGu, RowGu
                break
            case "21" ! CPI()
                cpi = dec($00$ + mvalue$) / 100
                FontFunc'PrintPageAsBitmap(bitmap, 'cpi'(cpi))
                result$ = FontFunc'GetMaximums$(bitmap)
                read data from result$,sep=":" to MaxColumns, MaxRows, MaxColGus, MaxRowGus, ColGu, RowGu
                break
        esac
    next x

    FontFunc'DestroyPageAsBitmap(bitmap)
    drop object FontFunc

    SearchedPage = ThisPage
    return Occurs
!
!
! SearchTable [ Occurrence, 1:9 ]
! 1 - Byte Offset to start of text or mnemonic
! 2 - Length of Match, (Text or mnemonic)
! 3 - Plain Text = 0, GUI = 1
! 4 - XLocation
! 5 - YLocation
! 6 - XPercent - Location as a percentage across the page
! 7 - YPercent - Location as a percentage across the page
! 8 - Reverse Video On/Off = 1/0 at replacement point
! 9 - Underline On/Off =1/0 at replacement point
!
! SearchTable$ [ Occurence, 1:4 ]
! 1 - Text being replaced or mnemonic 'TEXT'() being replaced
!   - This will include the highlight colour code embedded in
! 2 - Foreground Colour at replacement point
! 3 - Background Colour at replacement point
! 4 - Mode at replacement point
!
HighlightSearchItem:
    enter PageData$, (Element)

    if len(PageData$) = 0 or SearchTable[Element, 2] = 0 \
        then return 1

    Offset = SearchTable[Element, 1]
    Length = SearchTable[Element, 2]

    PageData$ = PageData$(1, Offset - 1) + \
                SearchTable$[Element, 1] + \
                PageData$(Offset + Length)

    return 0
