📄 htmlsectioncreator.cpp
字号:
/*----------------------------------------------------------------------
Copyright (c) 1998 Gipsysoft. All Rights Reserved.
Please see the file "licence.txt" for licencing details.
File: HTMLSectionCreator.cpp
Owner: russf@gipsysoft.com
Purpose: Section creator for the HTML display.
----------------------------------------------------------------------*/
#include "stdafx.h"
#include <math.h>
#include <ImgLib.h>
#include "HTMLParse.h"
#include "HTMLSectionCreator.h"
#include "HTMLTextSection.h"
#include "HTMLImageSection.h"
#include "HTMLHorizontalRuleSection.h"
#include "HTMLSection.h"
#include "HTMLTableSection.h"
#include "tableLayout.h"
#include "defaults.h"
CHTMLSectionCreator::CHTMLSectionCreator( CHTMLSection *psect, CDrawContext &dc, int nTop, int nLeft, int nRight, COLORREF crBack, bool bMeasuring, int nZoomLevel )
: m_psect( psect )
, m_dc( dc )
, m_nDefaultLeftMargin( nLeft )
, m_nDefaultRightMargin( nRight )
, m_crBack( crBack )
, m_nLeftMargin( nLeft )
, m_nRightMargin( nRight )
, m_nYPos( nTop )
, m_nTop( nTop )
, m_nPreviousParaSpaceBelow( 0 )
, m_nFirstShapeOnLine( 0 )
, m_nNextYPos( nTop )
, m_nWidth( 0 )
, m_bMeasuring( bMeasuring )
, m_nBaseLineShapeStartID( 0 )
, m_nLowestBaseline( 0 )
, m_algCurrentPargraph( CHTMLParse::algLeft )
, m_nZoomLevel( nZoomLevel )
, m_nXPos( 0 )
{
m_dc.SelectFont( g_defaults.m_strFontName, GetFontSizeAsPixels( m_dc.GetSafeHdc(), g_defaults.m_nFontSize, m_nZoomLevel ), FW_NORMAL, false, false, false, g_defaults.m_cCharSet );
}
CHTMLSectionCreator::~CHTMLSectionCreator()
{
}
void CHTMLSectionCreator::CarriageReturn()
//
// Recalcualtes the left and right margins for the current Y position and
// moves the X position to the new left margin.
{
if( GetCurrentShapeID() )
{
AdjustShapeBaselinesAndHorizontalAlignment();
}
m_nBaseLineShapeStartID = GetCurrentShapeID();
m_nLeftMargin = m_nDefaultLeftMargin;
while( m_stkLeftMargin.GetSize() )
{
MarginStackItem msi = m_stkLeftMargin.Top();
if( msi.nYExpiry >= m_nYPos )
{
m_nLeftMargin = msi.nMargin;
break;
}
else
{
m_stkLeftMargin.Pop();
}
}
m_nRightMargin = m_nDefaultRightMargin;
while( m_stkRightMargin.GetSize() )
{
MarginStackItem msi = m_stkRightMargin.Top();
if( msi.nYExpiry >= m_nYPos )
{
m_nRightMargin = msi.nMargin;
break;
}
else
{
m_stkRightMargin.Pop();
}
}
m_nXPos = m_nLeftMargin;
m_arrBaseline.RemoveAll();
m_nLowestBaseline = 0;
m_nFirstShapeOnLine = GetCurrentShapeID();
// If there is nothing in the margin stack, this is a good place for
// a page break.
if( m_dc.IsPrinting() && m_nLeftMargin == m_nDefaultLeftMargin && m_nRightMargin == m_nDefaultRightMargin)
{
m_psect->AddBreakSection(GetCurrentShapeID());
}
}
void CHTMLSectionCreator::Finished()
//
// Signals the end fo the document.
{
CarriageReturn();
m_nYPos = m_nNextYPos;
//
// Adjust the height of our document to take into account any images there may be on the left or right
// of the document and that may be taller than we have left the document
while( m_stkRightMargin.GetSize() )
{
MarginStackItem msi = m_stkRightMargin.Top();
if( msi.nYExpiry > m_nYPos )
m_nYPos = msi.nYExpiry;
m_stkRightMargin.Pop();
}
while( m_stkLeftMargin.GetSize() )
{
MarginStackItem msi = m_stkLeftMargin.Top();
if( msi.nYExpiry > m_nYPos )
m_nYPos = msi.nYExpiry;
m_stkLeftMargin.Pop();
}
if( m_dc.IsPrinting() )
m_psect->AddBreakSection(GetCurrentShapeID());
}
void CHTMLSectionCreator::NewParagraph( int nSpaceAbove, int nSpaceBelow, CHTMLParse::Align alg )
//
// New paragraph.
// Add on the space below from the previous paragraph and the space above for this paragraph.
{
CarriageReturn();
m_nYPos = m_nNextYPos;
if( m_nYPos != m_nTop )
{
m_nYPos += ( m_dc.GetCurrentFontHeight() * m_nPreviousParaSpaceBelow ) / 2;
m_nYPos += ( m_dc.GetCurrentFontHeight() * nSpaceAbove ) / 2;
}
m_algCurrentPargraph = alg;
m_nPreviousParaSpaceBelow = nSpaceBelow;
}
void CHTMLSectionCreator::AddTextPreformat( const StringClass &str, const HTMLFontDef &htmlfdef, COLORREF crFore, CHTMLSectionLink* pLink )
{
FontDef fdef( htmlfdef.m_strFont, htmlfdef.m_nSize, htmlfdef.m_nWeight, htmlfdef.m_bItalic, htmlfdef.m_bUnderline, htmlfdef.m_bStrike, m_cCharSet );
fdef.m_nSizePixels = GetFontSizeAsPixels( m_dc.GetSafeHdc(), htmlfdef.m_nSize, m_nZoomLevel );
if( pLink )
fdef.m_bUnderline = true;
m_dc.SelectFont( fdef );
LPCTSTR pcszTextLocal = str;
LPCTSTR pcszPrevious = str;
while( 1 )
{
if( *pcszTextLocal == '\r' || *pcszTextLocal == '\n' || *pcszTextLocal == '\000' )
{
const UINT uLength = pcszTextLocal - pcszPrevious;
const CSize size( m_dc.GetTextExtent( pcszPrevious, uLength ), m_dc.GetCurrentFontHeight() );
CHTMLTextSection *pText = new CHTMLTextSection( m_psect, pcszPrevious, uLength, fdef, crFore );
pText->Set( m_nXPos, m_nYPos, m_nXPos + size.cx, m_nYPos + size.cy );
AddBaseline( m_dc.GetCurrentFontBaseline() );
CarriageReturn();
m_nYPos = m_nNextYPos;
if( *pcszTextLocal == '\000' )
{
break;
}
if( *pcszTextLocal == '\r' && *( pcszTextLocal + 1 ) == '\n' )
{
*pcszTextLocal++;
}
pcszPrevious = pcszTextLocal + 1;
}
*pcszTextLocal++;
}
}
void CHTMLSectionCreator::AddText( const StringClass &str, const HTMLFontDef &htmlfdef, COLORREF crFore, CHTMLSectionLink* pLink )
{
FontDef fdef( htmlfdef.m_strFont, htmlfdef.m_nSize, htmlfdef.m_nWeight, htmlfdef.m_bItalic, htmlfdef.m_bUnderline, htmlfdef.m_bStrike, m_cCharSet );
fdef.m_nSizePixels = GetFontSizeAsPixels( m_dc.GetSafeHdc(), htmlfdef.m_nSize, m_nZoomLevel );
if( pLink )
fdef.m_bUnderline = true;
m_dc.SelectFont( fdef );
const int nCurrentFontHeight = m_dc.GetCurrentFontBaseline();
/*
try the text to see if it fits.
if it does then simply create a new object and bump along the XPos
if it does not then create an object with the least amount of data to fit
create a new line (CarriageReturn) and try again.
*/
LPCTSTR pcszTextLocal = str;
LPCTSTR pcszPrevious = str;
CSize size;
bool bDone = false;
while( !bDone )
{
CHTMLTextSection *pText = NULL;
// richg - 19990224 - Altered to indicate the the CDrawContext whether or not
// we are at the beginning of a line.
if( m_dc.GetTextFitWithWordWrap( m_nRightMargin - m_nXPos, pcszTextLocal, size, (bool)(m_nXPos == m_nLeftMargin) ) )
{
pText = new CHTMLTextSection( m_psect, pcszPrevious, pcszTextLocal - pcszPrevious, fdef, crFore );
pText->Set( m_nXPos, m_nYPos, m_nXPos + size.cx, m_nYPos + size.cy );
m_nXPos+=size.cx;
if( htmlfdef.m_nSup )
{
AddBaseline( nCurrentFontHeight + htmlfdef.m_nSup * nCurrentFontHeight / 3 );
}
else if( htmlfdef.m_nSub )
{
AddBaseline( nCurrentFontHeight - htmlfdef.m_nSub * nCurrentFontHeight / 3 );
}
else
{
AddBaseline( nCurrentFontHeight );
}
bDone = true;
}
else
{
if( pcszTextLocal - pcszPrevious > 0 && ( m_nLeftMargin == m_nXPos || _istspace( *pcszTextLocal ) ) )
{
pText = new CHTMLTextSection( m_psect, pcszPrevious, pcszTextLocal - pcszPrevious, fdef, crFore );
pText->Set( m_nXPos, m_nYPos, m_nXPos + size.cx, m_nYPos + size.cy );
if( htmlfdef.m_nSup )
{
AddBaseline( nCurrentFontHeight + htmlfdef.m_nSup * nCurrentFontHeight / 3 );
}
else if( htmlfdef.m_nSub )
{
AddBaseline( nCurrentFontHeight - htmlfdef.m_nSub * nCurrentFontHeight / 3 );
}
else
{
AddBaseline( nCurrentFontHeight );
}
}
else
{
pcszTextLocal = pcszPrevious;
}
CarriageReturn();
m_nYPos = m_nNextYPos;
// Skip over trailing spaces
while( *pcszTextLocal && _istspace( *pcszTextLocal ) ) pcszTextLocal++;
pcszPrevious = pcszTextLocal;
}
if( pText && pLink )
{
pText->SetAsLink( pLink );
}
}
}
int CHTMLSectionCreator::GetCurrentShapeID() const
{
return m_psect->GetSectionCount();
}
void CHTMLSectionCreator::AddBaseline( int nBaseline )
{
UINT nIndex = GetCurrentShapeID() - m_nBaseLineShapeStartID;
while( nIndex > m_arrBaseline.GetSize() )
m_arrBaseline.Add( -1 );
m_arrBaseline[ nIndex - 1 ] = nBaseline;
if( nBaseline > m_nLowestBaseline )
{
m_nLowestBaseline = nBaseline;
}
}
int CHTMLSectionCreator::GetBaseline( int nShape ) const
{
// richg - Made this a little more tolerant of error.
if (nShape < m_nBaseLineShapeStartID)
return -1;
const UINT index = nShape - m_nBaseLineShapeStartID;
if( index < m_arrBaseline.GetSize())
{
return m_arrBaseline[ index ];
}
return -1;
}
void CHTMLSectionCreator::AdjustShapeBaselinesAndHorizontalAlignment()
//
// Adjust the shapes baselines to allow for different heights and styles of text
{
int nHorizontalDelta = 0;
//
// Horizontally align the shapes.
//
// Get the horizontal extent of allof the shapes.
// if right aligned then move allthe shapes on the line by the difference
// between the width between the margins and the line extent.
// if centre aligned then move the shapes half the difference between the
// margins and the line extent.
if( !m_bMeasuring && m_algCurrentPargraph != CHTMLParse::algLeft && m_nFirstShapeOnLine != GetCurrentShapeID() )
{
const int nCurrentShape = GetCurrentShapeID() - 1;
const CSectionABC *psectFirst = m_psect->GetSectionAt( m_nFirstShapeOnLine );
const CSectionABC *psectSecond = m_psect->GetSectionAt( nCurrentShape );
const int nExtent = psectSecond->right - psectFirst->left;
const int nDocWidth = GetCurrentWidth();
const int nSpaceWidth = nDocWidth - nExtent;
if( m_algCurrentPargraph == CHTMLParse::algCentre )
{
// richg - 19990225 - Never shift left!
nHorizontalDelta = max( 0, nSpaceWidth / 2 );
}
else if( m_algCurrentPargraph == CHTMLParse::algRight )
{
// richg - 19990225 - Never shift left!
nHorizontalDelta = max( 0, nSpaceWidth );
}
}
//
// Minus one is a signal that the shapes are all the same size.
m_nNextYPos = m_nYPos;
const int nCurrentShapeID = GetCurrentShapeID();
if( m_nFirstShapeOnLine != nCurrentShapeID )
{
for( int n = m_nFirstShapeOnLine; n < nCurrentShapeID; n++ )
{
CSectionABC *pSect = m_psect->GetSectionAt( n );
const int nBaseline = GetBaseline( n );
if( nBaseline >= 0 )
{
const int nBaselineDelta = m_nLowestBaseline - nBaseline;
if( nBaselineDelta || nHorizontalDelta )
{
pSect->Offset( nHorizontalDelta, nBaselineDelta );
}
//
// We only use shapes that have a baseline
if( pSect->bottom > m_nNextYPos )
{
m_nNextYPos = pSect->bottom;
}
}
if( pSect->right > m_nWidth )
m_nWidth = pSect->right;
}
}
}
void CHTMLSectionCreator::AddImage( int nWidth, int nHeight, int nBorder, CImage *pImage, CHTMLParse::Align alg, CHTMLSectionLink* pLink )
{
ASSERT( pImage );
CSize size( pImage->GetSize() );
if( m_dc.IsPrinting() )
size = m_dc.Scale( size );
if( nWidth == 0 )
nWidth = size.cx;
else
nWidth = m_dc.ScaleX( nWidth );
if( nHeight == 0 )
nHeight = size.cy;
else
nHeight = m_dc.ScaleY( nHeight );
nWidth += m_dc.ScaleX(nBorder * 2);
nHeight += m_dc.ScaleY(nBorder * 2);
int nTop = m_nYPos;
int nBaseline = nHeight;
if( nWidth > m_nRightMargin - m_nXPos )
{
CarriageReturn();
m_nYPos = m_nNextYPos;
nTop = m_nYPos;
}
int nLeft = m_nXPos;
switch( alg )
{
case CHTMLParse::algBottom: break;
case CHTMLParse::algCentre:
case CHTMLParse::algMiddle:
nBaseline = nHeight / 2;
break;
case CHTMLParse::algTop:
nBaseline = m_dc.GetCurrentFontBaseline();
break;
case CHTMLParse::algLeft:
{
nLeft = m_nLeftMargin;
MarginStackItem msi = { nLeft + nWidth + g_defaults.m_nImageMargin, m_nYPos + nHeight };
m_stkLeftMargin.Push( msi );
if( m_nXPos == m_nLeftMargin )
m_nXPos = msi.nMargin;
m_nLeftMargin = msi.nMargin;
}
break;
case CHTMLParse::algRight:
{
nLeft = m_nRightMargin - nWidth - g_defaults.m_nImageMargin;
MarginStackItem msi = { nLeft, m_nYPos + nHeight };
m_stkRightMargin.Push( msi );
m_nRightMargin = msi.nMargin;
}
break;
}
CHTMLImageSection *pImageSection = new CHTMLImageSection( m_psect, pImage, nBorder );
pImageSection->Set( nLeft, nTop, nLeft + nWidth, nTop + nHeight );
if( pLink )
pImageSection->SetAsLink( pLink );
if( alg != CHTMLParse::algLeft && alg != CHTMLParse::algRight )
{
m_nXPos += nWidth;
AddBaseline( nBaseline );
}
}
void CHTMLSectionCreator::AddHorizontalRule( CHTMLParse::Align alg, int nSize, int nWidth, bool bNoShade, COLORREF crColor, CHTMLSectionLink* pLink )
//
// Horizontal ruler creates a space as tall as it is, also does a carriage return.
{
const int nDisplayWidth = GetCurrentWidth();
if( nWidth < 0 )
{
// If we are measuring, and looking atthe largest possible size,
// return some small number, as it is irrelevant!
if (m_bMeasuring && nDisplayWidth == knFindMaximumWidth)
nWidth = 1;
else
nWidth = int( float( nDisplayWidth ) / 100 * abs(nWidth) );
}
int nLeft = m_nLeftMargin;
if( !m_bMeasuring && nWidth != nDisplayWidth )
{
switch( alg )
{
case CHTMLParse::algRight:
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -