/**************************************************************
 *
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 *
 *************************************************************/



// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_sw.hxx"


#include <stdlib.h>
#include <hintids.hxx>
#include <svl/urihelper.hxx>
#include <rtl/tencinfo.h>
#include <vcl/wrkwin.hxx>
#include <sfx2/linkmgr.hxx>

#include <svtools/htmlcfg.hxx>
#include <vcl/svapp.hxx>
#include <i18npool/mslangid.hxx>
#include <sfx2/frmhtmlw.hxx>
#include <svx/xoutbmp.hxx>
#include <svx/htmlmode.hxx>
#include <editeng/lrspitem.hxx>
#include <editeng/colritem.hxx>
#include <editeng/brshitem.hxx>
#include <editeng/fontitem.hxx>
#include <editeng/scripttypeitem.hxx>
#include <editeng/langitem.hxx>
#include <svl/stritem.hxx>
#include <editeng/frmdiritem.hxx>

#include <com/sun/star/document/XDocumentPropertiesSupplier.hpp>
#include <com/sun/star/document/XDocumentProperties.hpp>
#include <com/sun/star/form/XFormsSupplier.hpp>
#include <com/sun/star/form/XForm.hpp>
#include <com/sun/star/form/XImageProducerSupplier.hpp>
#include <com/sun/star/form/XFormController.hpp>
#include <com/sun/star/container/XContainer.hpp>
#include <com/sun/star/container/XIndexContainer.hpp>
#include <com/sun/star/container/XSet.hpp>
#include <fmthdft.hxx>
#include <fmtfld.hxx>
#include <fmtpdsc.hxx>
#include <txatbase.hxx>
#include <frmatr.hxx>
#include <charfmt.hxx>
#include <docary.hxx>
#include <pam.hxx>
#include <doc.hxx>
#include <ndtxt.hxx>
#include <mdiexp.hxx>		// ...Percent()
#include <fltini.hxx>
#include <viewopt.hxx>
#include <IMark.hxx>		// fuer SwBookmark ...
#include <poolfmt.hxx>
#include <pagedesc.hxx>
#include <section.hxx>
#include <swtable.hxx>
#include <fldbas.hxx>
#include <fmtclds.hxx>
#ifndef _DOCSH_HXX
#include <docsh.hxx>
#endif
#include <wrthtml.hxx>
#include <htmlnum.hxx>
#include <htmlfly.hxx>
#include <swmodule.hxx>

#ifndef _STATSTR_HRC
#include <statstr.hrc>		// ResId fuer Statusleiste
#endif
#include <swerror.h>

#define MAX_INDENT_LEVEL 20

#if defined(UNX)
const sal_Char SwHTMLWriter::sNewLine = '\012';
#else
const sal_Char __FAR_DATA SwHTMLWriter::sNewLine[] = "\015\012";
#endif

static sal_Char __FAR_DATA sIndentTabs[MAX_INDENT_LEVEL+2] =
	"\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t";

SwHTMLWriter::SwHTMLWriter( const String& rBaseURL )
{
    SetBaseURL( rBaseURL );
	bFirstLine = sal_True;
	nBkmkTabPos = -1;
	pDfltColor = 0;
	nImgMapCnt = 1;
	pStartNdIdx = 0;
	pTemplate = 0;
	pNumRuleInfo = new SwHTMLNumRuleInfo;
	pNextNumRuleInfo = 0;
	pFootEndNotes = 0;
	pFmtFtn = 0;
	eDestEnc = RTL_TEXTENCODING_MS_1252;
	nDirection = FRMDIR_HORI_LEFT_TOP;
}


__EXPORT SwHTMLWriter::~SwHTMLWriter()
{
	delete pNumRuleInfo;
}

sal_uLong SwHTMLWriter::WriteStream()
{
	// neue Konfiguration setzen
	SvxHtmlOptions* pHtmlOptions = SvxHtmlOptions::Get();

	// die Fontgroessen 1-7
	aFontHeights[0] = pHtmlOptions->GetFontSize( 0 ) * 20;
	aFontHeights[1] = pHtmlOptions->GetFontSize( 1 ) * 20;
	aFontHeights[2] = pHtmlOptions->GetFontSize( 2 ) * 20;
	aFontHeights[3] = pHtmlOptions->GetFontSize( 3 ) * 20;
	aFontHeights[4] = pHtmlOptions->GetFontSize( 4 ) * 20;
	aFontHeights[5] = pHtmlOptions->GetFontSize( 5 ) * 20;
	aFontHeights[6] = pHtmlOptions->GetFontSize( 6 ) * 20;

	// ueberhaupt Styles ausgeben
	// (dann auch obere und untere Absatz-Abstaende)
	nExportMode = pHtmlOptions->GetExportMode();
	nHTMLMode = GetHtmlMode(0);
    if( HTML_CFG_WRITER==nExportMode ||
		HTML_CFG_NS40==nExportMode )
		nHTMLMode |= HTMLMODE_BLOCK_SPACER;

	if( HTML_CFG_WRITER==nExportMode || HTML_CFG_MSIE==nExportMode )
		nHTMLMode |= (HTMLMODE_FLOAT_FRAME | HTMLMODE_LSPACE_IN_NUMBUL);

	if( HTML_CFG_MSIE==nExportMode )
		nHTMLMode |= HTMLMODE_NBSP_IN_TABLES;

	if( HTML_CFG_WRITER==nExportMode || HTML_CFG_NS40==nExportMode ||
		HTML_CFG_MSIE==nExportMode )
		nHTMLMode |= HTMLMODE_ABS_POS_FLY|HTMLMODE_ABS_POS_DRAW;

	if( HTML_CFG_WRITER==nExportMode )
//		nHTMLMode |= HTMLMODE_FLY_MARGINS | HTMLMODE_FRSTLINE_IN_NUMBUL;
		nHTMLMode |= HTMLMODE_FLY_MARGINS;

	if( HTML_CFG_NS40==nExportMode )
		nHTMLMode |= HTMLMODE_BORDER_NONE;

	if( HTML_CFG_HTML32!=nExportMode )
		nHTMLMode |= HTMLMODE_FONT_GENERIC;

    if( HTML_CFG_NS40==nExportMode )
		nHTMLMode |= HTMLMODE_NO_CONTROL_CENTERING;

	bCfgOutStyles = IsHTMLMode(HTMLMODE_SOME_STYLES |
							   HTMLMODE_FULL_STYLES);
	bCfgNetscape4 = (HTML_CFG_NS40==nExportMode);

	if( IsHTMLMode(HTMLMODE_SOME_STYLES | HTMLMODE_FULL_STYLES) )
		nHTMLMode |= HTMLMODE_PRINT_EXT;

	const sal_Char *pHelpHack = getenv( "HelpEx" );
	if( pHelpHack )
	{
		ByteString aTmp( pHelpHack );
		if( aTmp.EqualsIgnoreCaseAscii( "Hilfe" ) )
			nHTMLMode |= HTMLMODE_NO_BR_AT_PAREND;
	}

	eCSS1Unit = (FieldUnit)SW_MOD()->GetMetric( pDoc->get(IDocumentSettingAccess::HTML_MODE) );

	sal_Bool bWriteUTF8 = bWriteClipboardDoc;
	eDestEnc = bWriteUTF8 ? RTL_TEXTENCODING_UTF8
						  : pHtmlOptions->GetTextEncoding();
	const sal_Char *pCharSet =
		rtl_getBestMimeCharsetFromTextEncoding( eDestEnc );
	eDestEnc = rtl_getTextEncodingFromMimeCharset( pCharSet );

	// fuer Netscape optimieren heisst Spacer- und Multicol ausgeben
//	bCfgMultiCol = pHtmlOptions->IsNetscape3();
//	bCfgSpacer = pHtmlOptions->IsNetscape3();

	// wenn Styles exportiert werden, wird ein Style einem HTML-Tag manchmal
	// vorgezogen, wenn nicht fuer Netscape exportiert wird
	// bCfgPreferStyles = bCfgOutStyles; // && !pHtmlOptions->IsNetscape3();

	// Nur noch fuer den MS-IE ziehen wir den Export von Styles vor.
	bCfgPreferStyles = HTML_CFG_MSIE==nExportMode;

	bCfgStarBasic = pHtmlOptions->IsStarBasic();

	bCfgFormFeed = !IsHTMLMode(HTMLMODE_PRINT_EXT);
	bCfgCpyLinkedGrfs = pHtmlOptions->IsSaveGraphicsLocal();

	// die HTML-Vorlage holen
	sal_Bool bOldHTMLMode = sal_False;
	sal_uInt16 nOldTxtFmtCollCnt = 0, nOldCharFmtCnt = 0;

	ASSERT( !pTemplate, "Wo kommt denn die HTML-Vorlage hier her?" );
	pTemplate = ((HTMLReader*)ReadHTML)->GetTemplateDoc();
	if( pTemplate )
	{
		pTemplate->acquire();
		bOldHTMLMode = pTemplate->get(IDocumentSettingAccess::HTML_MODE);
		pTemplate->set(IDocumentSettingAccess::HTML_MODE, true);

		nOldTxtFmtCollCnt = pTemplate->GetTxtFmtColls()->Count();
		nOldCharFmtCnt = pTemplate->GetCharFmts()->Count();
	}

	if( bShowProgress )
		::StartProgress( STR_STATSTR_W4WWRITE, 0, pDoc->GetNodes().Count(),
						 pDoc->GetDocShell());

	pDfltColor = 0;
	pFootEndNotes = 0;
	pFmtFtn = 0;
	bOutTable = bOutHeader = bOutFooter = bOutFlyFrame = sal_False;
	pxFormComps = 0;
	nFormCntrlCnt = 0;
	bPreserveForm = sal_False;
	bClearLeft = bClearRight = sal_False;
	bLFPossible = sal_False;

	nLeftMargin = nDfltLeftMargin = nDfltRightMargin = 0;
	nDfltTopMargin = nDfltBottomMargin = 0;
	nFirstLineIndent = nDfltFirstLineIndent = 0;
	bPoolCollTextModified = sal_False;
	bFirstCSS1Property = bFirstCSS1Rule = sal_False;
	bCSS1IgnoreFirstPageDesc = sal_False;
	nIndentLvl = 0;
	nWhishLineLen = 70;
	nLastLFPos = 0;
	nDefListLvl = 0;
	nDefListMargin = ((pTemplate && !bCfgOutStyles) ? pTemplate : pDoc)
        ->GetTxtCollFromPool( RES_POOLCOLL_HTML_DD, false )
		->GetLRSpace().GetTxtLeft();
	nHeaderFooterSpace = 0;
	nTxtAttrsToIgnore = 0;
	nCSS1OutMode = 0;
	sal_uInt16 nScript = SvtLanguageOptions::GetScriptTypeOfLanguage(
			static_cast< LanguageType >( GetAppLanguage() ) );
	switch( nScript )
	{
	case SCRIPTTYPE_ASIAN:
		nCSS1Script = CSS1_OUTMODE_CJK;
		break;
	case SCRIPTTYPE_COMPLEX:
		nCSS1Script = CSS1_OUTMODE_CTL;
		break;
	default:
		nCSS1Script = CSS1_OUTMODE_WESTERN;
		break;
	}
	eLang = ((const SvxLanguageItem&)pDoc
			->GetDefault(GetLangWhichIdFromScript(nCSS1Script))).GetLanguage();

	nFootNote = nEndNote = 0;

	nWarn = 0;
	GetNumInfo().Clear();
	pNextNumRuleInfo = 0;

	ByteString aStartTags;

	// Tabellen und Bereiche am Doc.-Anfang beachten
	{
		SwTableNode * pTNd = pCurPam->GetNode()->FindTableNode();
		if( pTNd && bWriteAll )
		{
			// mit dem Tabellen-Node anfangen !!
			pCurPam->GetPoint()->nNode = *pTNd;

			if( bWriteOnlyFirstTable )
				pCurPam->GetMark()->nNode = *pTNd->EndOfSectionNode();
		}

		// erster Node (der einen Seitenumbruch enthalten darf)
		pStartNdIdx = new SwNodeIndex( pCurPam->GetPoint()->nNode );

		SwSectionNode * pSNd = pCurPam->GetNode()->FindSectionNode();
		while( pSNd )
		{
			if( bWriteAll )
			{
				// mit dem Section-Node anfangen !!
				pCurPam->GetPoint()->nNode = *pSNd;
			}
			else
			{
				ASSERT( FILE_LINK_SECTION != pSNd->GetSection().GetType(),
						"Export gelinkter Bereiche am Dok-Anfang ist nicht implemntiert" );

				// nur das Tag fuer die Section merken
				ByteString aName;
                HTMLOutFuncs::ConvertStringToHTML(
                    pSNd->GetSection().GetSectionName(),
												   aName, eDestEnc, &aNonConvertableCharacters );

				ByteString sOut( '<' );
				(((((((sOut += OOO_STRING_SVTOOLS_HTML_division)
					+= ' ') += OOO_STRING_SVTOOLS_HTML_O_id) += "=\"")
				    += aName) += '\"')
					+= '>')	+= aStartTags;

				aStartTags = sOut;
			}
			// FindSectionNode() an einem SectionNode liefert den selben!
            pSNd = pSNd->StartOfSectionNode()->FindSectionNode();
		}
	}


	// Tabelle fuer die freifliegenden Rahmen erzeugen, aber nur wenn
	// das gesamte Dokument geschrieben wird
	pHTMLPosFlyFrms = 0;
	CollectFlyFrms();
	nLastParaToken = 0;
	GetControls();
	CollectLinkTargets();

	sal_uInt16 nHeaderAttrs = 0;
	pCurrPageDesc = MakeHeader( nHeaderAttrs );

	bLFPossible = sal_True;

	// Formulare, die nur HiddenControls enthalten ausgeben.
	OutHiddenForms();

	if( aStartTags.Len() )
		Strm() << aStartTags.GetBuffer();

	const SfxPoolItem *pItem;
	const SfxItemSet& rPageItemSet = pCurrPageDesc->GetMaster().GetAttrSet();
	if( !bWriteClipboardDoc && pDoc->GetDocShell() &&
         (!pDoc->get(IDocumentSettingAccess::HTML_MODE) &&
          !pDoc->get(IDocumentSettingAccess::BROWSE_MODE)) &&
		SFX_ITEM_SET == rPageItemSet.GetItemState( RES_HEADER, sal_True, &pItem) )
	{
		const SwFrmFmt *pHeaderFmt =
			((const SwFmtHeader *)pItem)->GetHeaderFmt();
		if( pHeaderFmt )
			OutHTML_HeaderFooter( *this, *pHeaderFmt, sal_True );
	}

	nTxtAttrsToIgnore = nHeaderAttrs;
	Out_SwDoc( pOrigPam );
	nTxtAttrsToIgnore = 0;

	if( pxFormComps && pxFormComps->is() )
		OutForm( sal_False, *pxFormComps );

	if( pFootEndNotes )
		OutFootEndNotes();

	if( !bWriteClipboardDoc && pDoc->GetDocShell() &&
        (!pDoc->get(IDocumentSettingAccess::HTML_MODE) && !pDoc->get(IDocumentSettingAccess::BROWSE_MODE))  &&
		SFX_ITEM_SET == rPageItemSet.GetItemState( RES_FOOTER, sal_True, &pItem) )
	{
		const SwFrmFmt *pFooterFmt =
			((const SwFmtFooter *)pItem)->GetFooterFmt();
		if( pFooterFmt )
			OutHTML_HeaderFooter( *this, *pFooterFmt, sal_False );
	}

	if( bLFPossible )
		OutNewLine();
	HTMLOutFuncs::Out_AsciiTag( Strm(), OOO_STRING_SVTOOLS_HTML_body, sal_False );
	OutNewLine();
	HTMLOutFuncs::Out_AsciiTag( Strm(), OOO_STRING_SVTOOLS_HTML_html, sal_False );

	// loesche die Tabelle mit den freifliegenden Rahmen
	sal_uInt16 i;
	ASSERT( !pHTMLPosFlyFrms, "Wurden nicht alle Rahmen ausgegeben" );
	if( pHTMLPosFlyFrms )
	{
		pHTMLPosFlyFrms->DeleteAndDestroy( 0, pHTMLPosFlyFrms->Count() );
		delete pHTMLPosFlyFrms;
		pHTMLPosFlyFrms = 0;
	}

	if( aHTMLControls.Count() )
		aHTMLControls.DeleteAndDestroy( sal_uInt16(0), aHTMLControls.Count() );

	if( aChrFmtInfos.Count() )
		aChrFmtInfos.DeleteAndDestroy( sal_uInt16(0), aChrFmtInfos.Count() );

	if( aTxtCollInfos.Count() )
		aTxtCollInfos.DeleteAndDestroy( sal_uInt16(0), aTxtCollInfos.Count() );

	if( aImgMapNames.Count() )
		aImgMapNames.DeleteAndDestroy( sal_uInt16(0), aImgMapNames.Count() );

	if( aImplicitMarks.Count() )
		aImplicitMarks.DeleteAndDestroy( sal_uInt16(0), aImplicitMarks.Count() );

	if( aOutlineMarks.Count() )
		aOutlineMarks.DeleteAndDestroy( sal_uInt16(0), aOutlineMarks.Count() );

	if( aOutlineMarkPoss.Count() )
		aOutlineMarkPoss.Remove( sal_uInt16(0), aOutlineMarkPoss.Count() );

	if( aNumRuleNames.Count() )
		aNumRuleNames.DeleteAndDestroy( sal_uInt16(0), aNumRuleNames.Count() );

	if( aScriptParaStyles.Count() )
		aScriptParaStyles.DeleteAndDestroy( sal_uInt16(0), aScriptParaStyles.Count() );
	if( aScriptTextStyles.Count() )
		aScriptTextStyles.DeleteAndDestroy( sal_uInt16(0), aScriptTextStyles.Count() );

	delete pDfltColor;
	pDfltColor = 0;

	delete pStartNdIdx;
	pStartNdIdx = 0;

	delete pxFormComps;
	pxFormComps = 0;

	ASSERT( !pFootEndNotes,
			"SwHTMLWriter::Write: Ftns nicht durch OutFootEndNotes geloescht" );

	pCurrPageDesc = 0;

	ClearNextNumInfo();

	for( i=0; i<MAXLEVEL; i++ )
		aBulletGrfs[i].Erase();

	aNonConvertableCharacters.Erase();

	if( bShowProgress )
		::EndProgress( pDoc->GetDocShell() );

	if( pTemplate )
	{
		// Waehrend des Exports angelegte Zeichen- und Abastzvorlagen
		// loeschen
		sal_uInt16 nTxtFmtCollCnt = pTemplate->GetTxtFmtColls()->Count();
		while( nTxtFmtCollCnt > nOldTxtFmtCollCnt )
			pTemplate->DelTxtFmtColl( --nTxtFmtCollCnt );
		ASSERT( pTemplate->GetTxtFmtColls()->Count() == nOldTxtFmtCollCnt,
				"falsche Anzahl TxtFmtColls geloescht" );

		sal_uInt16 nCharFmtCnt = pTemplate->GetCharFmts()->Count();
		while( nCharFmtCnt > nOldCharFmtCnt )
			pTemplate->DelCharFmt( --nCharFmtCnt );
		ASSERT( pTemplate->GetCharFmts()->Count() == nOldCharFmtCnt,
				"falsche Anzahl CharFmts geloescht" );

		// HTML-Modus wieder restaurieren
		pTemplate->set(IDocumentSettingAccess::HTML_MODE, bOldHTMLMode);

		if( 0 == pTemplate->release() )
			delete pTemplate;

		pTemplate = 0;
	}

	return nWarn;
}

const SwFmtCol *lcl_html_GetFmtCol( const SwHTMLWriter& rHTMLWrt,
							   		const SwSection& rSection,
							   		const SwSectionFmt& rFmt )
{
	const SwFmtCol *pCol = 0;

	const SfxPoolItem* pItem;
	if( rHTMLWrt.IsHTMLMode( HTMLMODE_FRM_COLUMNS ) &&
		FILE_LINK_SECTION != rSection.GetType() &&
		SFX_ITEM_SET == rFmt.GetAttrSet().GetItemState(RES_COL,sal_False,&pItem) &&
		((const SwFmtCol *)pItem)->GetNumCols() > 1 )
	{
		pCol = (const SwFmtCol *)pItem;
	}

	return pCol;
}

sal_Bool lcl_html_IsMultiColStart( const SwHTMLWriter& rHTMLWrt, sal_uLong nIndex )
{
	sal_Bool bRet = sal_False;
	const SwSectionNode *pSectNd =
		rHTMLWrt.pDoc->GetNodes()[nIndex]->GetSectionNode();
	if( pSectNd )
	{
		const SwSection& rSection = pSectNd->GetSection();
		const SwSectionFmt *pFmt = rSection.GetFmt();
		if( pFmt && lcl_html_GetFmtCol( rHTMLWrt, rSection, *pFmt ) )
			bRet = sal_True;
	}

	return bRet;
}

sal_Bool lcl_html_IsMultiColEnd( const SwHTMLWriter& rHTMLWrt, sal_uLong nIndex )
{
	sal_Bool bRet = sal_False;
	const SwEndNode *pEndNd = rHTMLWrt.pDoc->GetNodes()[nIndex]->GetEndNode();
	if( pEndNd )
		bRet = lcl_html_IsMultiColStart( rHTMLWrt,
										 pEndNd->StartOfSectionIndex() );

	return bRet;
}


void lcl_html_OutSectionStartTag( SwHTMLWriter& rHTMLWrt,
							   	  const SwSection& rSection,
							   	  const SwSectionFmt& rFmt,
								  const SwFmtCol *pCol,
								  sal_Bool bContinued=sal_False )
{
	ASSERT( pCol || !bContinued, "Continuation of DIV" );

	if( rHTMLWrt.bLFPossible )
		rHTMLWrt.OutNewLine();

	const sal_Char *pTag = pCol ? OOO_STRING_SVTOOLS_HTML_multicol : OOO_STRING_SVTOOLS_HTML_division;

	ByteString sOut( '<' );
	sOut += pTag;

    const String& rName = rSection.GetSectionName();
	if( rName.Len() && !bContinued )
	{
		((sOut += ' ') += OOO_STRING_SVTOOLS_HTML_O_id) += "=\"";
		rHTMLWrt.Strm() << sOut.GetBuffer();
		HTMLOutFuncs::Out_String( rHTMLWrt.Strm(), rName, rHTMLWrt.eDestEnc, &rHTMLWrt.aNonConvertableCharacters );
		sOut = '\"';
	}

	sal_uInt16 nDir = rHTMLWrt.GetHTMLDirection( rFmt.GetAttrSet() );
	rHTMLWrt.Strm() << sOut.GetBuffer();
	sOut.Erase();
	rHTMLWrt.OutDirection( nDir );

	if( FILE_LINK_SECTION == rSection.GetType() )
	{
		((sOut += ' ') += OOO_STRING_SVTOOLS_HTML_O_href) += "=\"";
		rHTMLWrt.Strm() << sOut.GetBuffer();

		const String& aFName = rSection.GetLinkFileName();
        String aURL( aFName.GetToken(0,sfx2::cTokenSeperator) );
        String aFilter( aFName.GetToken(1,sfx2::cTokenSeperator) );
        String aSection( aFName.GetToken(2,sfx2::cTokenSeperator) );

        String aEncURL( URIHelper::simpleNormalizedMakeRelative(rHTMLWrt.GetBaseURL(), aURL ) );
		sal_Unicode cDelim = 255U;
		sal_Bool bURLContainsDelim =
			(STRING_NOTFOUND != aEncURL.Search( cDelim ) );

		HTMLOutFuncs::Out_String( rHTMLWrt.Strm(), aEncURL,
								  rHTMLWrt.eDestEnc,
								  &rHTMLWrt.aNonConvertableCharacters );
		const sal_Char *pDelim = "&#255;";
		if( aFilter.Len() || aSection.Len() || bURLContainsDelim )
			rHTMLWrt.Strm() << pDelim;
		if( aFilter.Len() )
			HTMLOutFuncs::Out_String( rHTMLWrt.Strm(), aFilter,
									  rHTMLWrt.eDestEnc, &rHTMLWrt.aNonConvertableCharacters );
		if( aSection.Len() || bURLContainsDelim  )
				rHTMLWrt.Strm() << pDelim;
		if( aSection.Len() )
		{
			xub_StrLen nPos = aSection.Search( '%' );
			while( STRING_NOTFOUND != nPos )
			{
				aSection.Erase( nPos, 1 );
				aSection.InsertAscii( "%25", nPos );
				nPos = aSection.Search( '%', nPos+3 );
			}
			nPos = aSection.Search( cDelim );
			while( STRING_NOTFOUND != nPos )
			{
				aSection.Erase( nPos, 1 );
				aSection.InsertAscii( "%FF", nPos );
				nPos = aSection.Search( cDelim, nPos+3 );
			}
			HTMLOutFuncs::Out_String( rHTMLWrt.Strm(), aSection,
									  rHTMLWrt.eDestEnc, &rHTMLWrt.aNonConvertableCharacters );
		}
		sOut = '\"';
	}
	else if( pCol )
	{
		(((sOut += ' ') += OOO_STRING_SVTOOLS_HTML_O_cols) += '=')
			+= ByteString::CreateFromInt32( pCol->GetNumCols() );

		// minimum gutter width
		sal_uInt16 nGutter = pCol->GetGutterWidth( sal_True );
		if( nGutter!=USHRT_MAX )
		{
			if( nGutter && Application::GetDefaultDevice() )
			{
				nGutter = (sal_uInt16)Application::GetDefaultDevice()
								->LogicToPixel( Size(nGutter,0),
												MapMode(MAP_TWIP) ).Width();
			}
			(((sOut += ' ') += OOO_STRING_SVTOOLS_HTML_O_gutter) += '=')
				+= ByteString::CreateFromInt32( nGutter );
		}
	}

	rHTMLWrt.Strm() << sOut.GetBuffer();
	if( rHTMLWrt.IsHTMLMode( rHTMLWrt.bCfgOutStyles ) )
		rHTMLWrt.OutCSS1_SectionFmtOptions( rFmt );

	rHTMLWrt.Strm() << '>';

	rHTMLWrt.bLFPossible = sal_True;
	if( rName.Len() && !bContinued )
		rHTMLWrt.OutImplicitMark( rName, pMarkToRegion );

	rHTMLWrt.IncIndentLevel();
}

void lcl_html_OutSectionEndTag( SwHTMLWriter& rHTMLWrt,
								const SwFmtCol *pCol )
{
	const sal_Char *pTag = pCol ? OOO_STRING_SVTOOLS_HTML_multicol : OOO_STRING_SVTOOLS_HTML_division;

	rHTMLWrt.DecIndentLevel();
	if( rHTMLWrt.bLFPossible )
		rHTMLWrt.OutNewLine();
	HTMLOutFuncs::Out_AsciiTag( rHTMLWrt.Strm(), pTag, sal_False );
	rHTMLWrt.bLFPossible = sal_True;
}

static Writer& OutHTML_Section( Writer& rWrt, const SwSectionNode& rSectNd )
{
	SwHTMLWriter& rHTMLWrt = (SwHTMLWriter&)rWrt;

	// End <PRE> and any <DL>, because a definition list's level may
	// change inside the section.
	rHTMLWrt.ChangeParaToken( 0 );
	rHTMLWrt.OutAndSetDefList( 0 );

	const SwSection& rSection = rSectNd.GetSection();
	const SwSectionFmt *pFmt = rSection.GetFmt();
	ASSERT( pFmt, "Section without a format?" );

	sal_Bool bStartTag = sal_True;
	sal_Bool bEndTag = sal_True;
	const SwSectionFmt *pSurrFmt = 0;
	const SwSectionNode *pSurrSectNd = 0;
	const SwSection *pSurrSection = 0;
	const SwFmtCol *pSurrCol = 0;

	sal_uInt32 nSectSttIdx = rSectNd.GetIndex();
	sal_uInt32 nSectEndIdx = rSectNd.EndOfSectionIndex();
	const SwFmtCol *pCol = lcl_html_GetFmtCol( rHTMLWrt, rSection, *pFmt );
	if( pCol )
	{
		// If the next node is a columned section node, too, don't export
		// an empty section.
		if( lcl_html_IsMultiColStart( rHTMLWrt, nSectSttIdx+1 ) )
			bStartTag = sal_False;

		// The same applies if the section end with another columned section.
		if( lcl_html_IsMultiColEnd( rHTMLWrt, nSectEndIdx-1 ) )
			bEndTag = sal_False;

		//.is there a columned section around this one?
        const SwStartNode *pSttNd = rSectNd.StartOfSectionNode();
		if( pSttNd )
		{
			pSurrSectNd = pSttNd->FindSectionNode();
			if( pSurrSectNd )
			{
				const SwStartNode *pBoxSttNd = pSttNd->FindTableBoxStartNode();
				if( !pBoxSttNd ||
					pBoxSttNd->GetIndex() < pSurrSectNd->GetIndex() )
				{
					pSurrSection = &pSurrSectNd->GetSection();
					pSurrFmt = pSurrSection->GetFmt();
					if( pSurrFmt )
						pSurrCol = lcl_html_GetFmtCol( rHTMLWrt, *pSurrSection,
													   *pSurrFmt );
				}
			}
		}
	}

	// The surrounding section must be closed before the current one is
	// opened, except that it start immediately before the current one or
	// another end immediately before the current one
	if( pSurrCol && nSectSttIdx - pSurrSectNd->GetIndex() > 1 &&
		!lcl_html_IsMultiColEnd( rHTMLWrt, nSectSttIdx-1 ) )
		lcl_html_OutSectionEndTag( rHTMLWrt, pSurrCol );

	if( bStartTag )
		lcl_html_OutSectionStartTag( rHTMLWrt, rSection, *pFmt, pCol );

	{
		HTMLSaveData aSaveData( rHTMLWrt,
			rHTMLWrt.pCurPam->GetPoint()->nNode.GetIndex()+1,
			rSectNd.EndOfSectionIndex(),
			sal_False, pFmt );
		rHTMLWrt.Out_SwDoc( rHTMLWrt.pCurPam );
	}

	rHTMLWrt.pCurPam->GetPoint()->nNode = *rSectNd.EndOfSectionNode();

	if( bEndTag )
		lcl_html_OutSectionEndTag( rHTMLWrt, pCol );

	// The surrounding section must be started again, except that it ends
	// immediately behind the current one.
	if( pSurrCol &&
		pSurrSectNd->EndOfSectionIndex() - nSectEndIdx > 1 &&
		!lcl_html_IsMultiColStart( rHTMLWrt, nSectEndIdx+1 ) )
		lcl_html_OutSectionStartTag( rHTMLWrt, *pSurrSection, *pSurrFmt,
									 pSurrCol, sal_True );

	return rWrt;
}

void SwHTMLWriter::Out_SwDoc( SwPaM* pPam )
{
	sal_Bool bSaveWriteAll = bWriteAll;		// sichern

    // suche die naechste text::Bookmark-Position aus der text::Bookmark-Tabelle
	nBkmkTabPos = bWriteAll ? FindPos_Bkmk( *pCurPam->GetPoint() ) : -1;

	// gebe alle Bereiche des Pams in das HTML-File aus.
	do {
		bWriteAll = bSaveWriteAll;
		bFirstLine = sal_True;

		// suche den ersten am Pam-auszugebenen FlyFrame
		// fehlt noch:

		while( pCurPam->GetPoint()->nNode.GetIndex() < pCurPam->GetMark()->nNode.GetIndex() ||
			  (pCurPam->GetPoint()->nNode.GetIndex() == pCurPam->GetMark()->nNode.GetIndex() &&
			   pCurPam->GetPoint()->nContent.GetIndex() <= pCurPam->GetMark()->nContent.GetIndex()) )
		{
			SwNode * pNd = pCurPam->GetNode();

			ASSERT( !(pNd->IsGrfNode() || pNd->IsOLENode()),
					"Grf- oder OLE-Node hier unerwartet" );
			if( pNd->IsTxtNode() )
			{
				SwTxtNode* pTxtNd = pNd->GetTxtNode();

				if( !bFirstLine )
					pCurPam->GetPoint()->nContent.Assign( pTxtNd, 0 );

				OutHTML_SwTxtNode( *this, *pTxtNd );
			}
			else if( pNd->IsTableNode() )
			{
				OutHTML_SwTblNode( *this, *pNd->GetTableNode(), 0 );
                nBkmkTabPos = bWriteAll ? FindPos_Bkmk( *pCurPam->GetPoint() ) : -1;
			}
			else if( pNd->IsSectionNode() )
			{
				OutHTML_Section( *this, *pNd->GetSectionNode() );
                nBkmkTabPos = bWriteAll ? FindPos_Bkmk( *pCurPam->GetPoint() ) : -1;
			}
			else if( pNd == &pDoc->GetNodes().GetEndOfContent() )
				break;

			pCurPam->GetPoint()->nNode++; 	// Bewegen
			sal_uInt32 nPos = pCurPam->GetPoint()->nNode.GetIndex();

			if( bShowProgress )
				::SetProgressState( nPos, pDoc->GetDocShell() );   // Wie weit ?

			/* sollen nur die Selectierten Bereiche gesichert werden, so
			 * duerfen nur die vollstaendigen Nodes gespeichert werde,
			 * d.H. der 1. und n. Node teilweise, der 2. bis n-1. Node
			 * vollstaendig. (vollstaendig heisst mit allen Formaten! )
			 */
			bWriteAll = bSaveWriteAll ||
						nPos != pCurPam->GetMark()->nNode.GetIndex();
			bFirstLine = sal_False;
			bOutFooter = sal_False; // Nach einem Node keine Fusszeile mehr
		}

		ChangeParaToken( 0 ); // MIB 8.7.97: Machen wir jetzt hier und nicht
							  // beim Aufrufer
		OutAndSetDefList( 0 );

	} while( CopyNextPam( &pPam ) );		// bis alle PaM's bearbeitet

	bWriteAll = bSaveWriteAll;			// wieder auf alten Wert zurueck
}


// schreibe die StyleTabelle, algemeine Angaben,Header/Footer/Footnotes
static void OutBodyColor( const sal_Char *pTag, const SwFmt *pFmt,
						  SwHTMLWriter& rHWrt )
{
	const SwFmt *pRefFmt = 0;

	if( rHWrt.pTemplate )
		pRefFmt = SwHTMLWriter::GetTemplateFmt( pFmt->GetPoolFmtId(),
												rHWrt.pTemplate );

	const SvxColorItem *pColorItem = 0;

	const SfxItemSet& rItemSet = pFmt->GetAttrSet();
    const SfxPoolItem *pRefItem = 0, *pItem = 0;
	sal_Bool bItemSet = SFX_ITEM_SET == rItemSet.GetItemState( RES_CHRATR_COLOR,
														   sal_True, &pItem);
	sal_Bool bRefItemSet = pRefFmt &&
		SFX_ITEM_SET == pRefFmt->GetAttrSet().GetItemState( RES_CHRATR_COLOR,
															sal_True, &pRefItem);
	if( bItemSet )
	{
		// wenn das Item nur in der Vorlage des aktuellen Doks gesetzt
		// ist oder einen anderen Wert hat, als in der HTML-Vorlage,
		// wird es gesetzt
		const SvxColorItem *pCItem = (const SvxColorItem*)pItem;

		if( !bRefItemSet )
		{
			pColorItem = pCItem;
		}
		else
		{
			Color aColor( pCItem->GetValue() );
			if( COL_AUTO == aColor.GetColor() )
				aColor.SetColor( COL_BLACK );

			Color aRefColor( ((const SvxColorItem*)pRefItem)->GetValue() );
			if( COL_AUTO == aRefColor.GetColor() )
				aRefColor.SetColor( COL_BLACK );

			if( !aColor.IsRGBEqual( aRefColor ) )
				pColorItem = pCItem;
		}
	}
	else if( bRefItemSet )
	{
		// Das Item war in der HTML-Vorlage noch gesetzt, also geben wir
		// das Default aus
		pColorItem = (const SvxColorItem*)&rItemSet.GetPool()
										->GetDefaultItem( RES_CHRATR_COLOR );
	}

	if( pColorItem )
	{
		ByteString sOut( ' ' );
		(sOut += pTag) += '=';
		rHWrt.Strm() << sOut.GetBuffer();
		Color aColor( pColorItem->GetValue() );
		if( COL_AUTO == aColor.GetColor() )
			aColor.SetColor( COL_BLACK );
		HTMLOutFuncs::Out_Color( rHWrt.Strm(), aColor, rHWrt.eDestEnc );
		if( RES_POOLCOLL_STANDARD==pFmt->GetPoolFmtId() )
			rHWrt.pDfltColor = new Color( aColor );
	}
}

sal_uInt16 SwHTMLWriter::OutHeaderAttrs()
{
    sal_uLong nIdx = pCurPam->GetPoint()->nNode.GetIndex();
    sal_uLong nEndIdx = pCurPam->GetMark()->nNode.GetIndex();

    SwTxtNode *pTxtNd = 0;
    while( nIdx<=nEndIdx &&
        0==(pTxtNd=pDoc->GetNodes()[nIdx]->GetTxtNode()) )
        nIdx++;

    ASSERT( pTxtNd, "Kein Text-Node gefunden" );
    if( !pTxtNd || !pTxtNd->HasHints() )
        return 0;

    sal_uInt16 nAttrs = 0;
    sal_uInt16 nCntAttr = pTxtNd->GetSwpHints().Count();
    xub_StrLen nOldPos = 0;
    for( sal_uInt16 i=0; i<nCntAttr; i++ )
    {
        const SwTxtAttr *pHt = pTxtNd->GetSwpHints()[i];
        if( !pHt->End() )
        {
            xub_StrLen nPos = *pHt->GetStart();
            if( nPos-nOldPos > 1
                || ( pHt->Which() != RES_TXTATR_FIELD
                     && pHt->Which() != RES_TXTATR_ANNOTATION ) )
                break;

            const sal_uInt16 nFldWhich =
                ((const SwFmtFld&)pHt->GetAttr()).GetField()->GetTyp()->Which();
            if( RES_POSTITFLD!=nFldWhich &&
                RES_SCRIPTFLD!=nFldWhich )
                break;

            OutNewLine();
            OutHTML_SwFmtFld( *this, pHt->GetAttr() );
            nOldPos = nPos;
            nAttrs++;
        }
    }

    return nAttrs;
}

const SwPageDesc *SwHTMLWriter::MakeHeader( sal_uInt16 &rHeaderAttrs )
{
	ByteString sOut( OOO_STRING_SVTOOLS_HTML_doctype );
	(sOut += ' ') +=
		(HTML_CFG_HTML32==nExportMode ? OOO_STRING_SVTOOLS_HTML_doctype32
		 							  : OOO_STRING_SVTOOLS_HTML_doctype40);
	HTMLOutFuncs::Out_AsciiTag( Strm(), sOut.GetBuffer() );

	// baue den Vorspann
	OutNewLine();
	HTMLOutFuncs::Out_AsciiTag( Strm(), OOO_STRING_SVTOOLS_HTML_html );

	OutNewLine();
	HTMLOutFuncs::Out_AsciiTag( Strm(), OOO_STRING_SVTOOLS_HTML_head );

	IncIndentLevel();	// Inhalt von <HEAD> einruecken

	// DokumentInfo
	ByteString sIndent;
	GetIndentString( sIndent );
//	OutNewLine();
    using namespace ::com::sun::star;
    uno::Reference<document::XDocumentProperties> xDocProps;
    SwDocShell *pDocShell(pDoc->GetDocShell());
    if (pDocShell) {
        uno::Reference<document::XDocumentPropertiesSupplier> xDPS(
            pDocShell->GetModel(), uno::UNO_QUERY_THROW);
        xDocProps.set(xDPS->getDocumentProperties());
    }

    // xDocProps may be null here (when copying)
    SfxFrameHTMLWriter::Out_DocInfo( Strm(), GetBaseURL(), xDocProps,
                                     sIndent.GetBuffer(), eDestEnc,
                                     &aNonConvertableCharacters );

	// Kommentare und Meta-Tags des ersten Absatzes
	rHeaderAttrs = OutHeaderAttrs();

	OutFootEndNoteInfo();

	const SwPageDesc *pPageDesc = 0;
	//if( !pDoc->IsHTMLMode() )
	//{
		// In Nicht-HTML-Dokumenten wird die erste gesetzte Seitenvorlage
		// exportiert und wenn keine gesetzt ist die Standard-Vorlage
		sal_uLong nNodeIdx = pCurPam->GetPoint()->nNode.GetIndex();

		while( nNodeIdx < pDoc->GetNodes().Count() )
		{
			SwNode *pNd = pDoc->GetNodes()[ nNodeIdx ];
			if( pNd->IsCntntNode() )
			{
				pPageDesc = ((const SwFmtPageDesc &)pNd->GetCntntNode()
					->GetAttr(RES_PAGEDESC)).GetPageDesc();
				break;
			}
			else if( pNd->IsTableNode() )
			{
				pPageDesc = pNd->GetTableNode()->GetTable().GetFrmFmt()
							   ->GetPageDesc().GetPageDesc();
				break;
			}

			nNodeIdx++;
		}

		if( !pPageDesc )
			pPageDesc = &const_cast<const SwDoc *>(pDoc)->GetPageDesc( 0 );
	//}
	//else
	//{
		// In HTML-Dokumenten nehmen wir immer die HTML-Vorlage
	//	pPageDesc = pDoc->GetPageDescFromPool( RES_POOLPAGE_HTML );
	//}

	// und nun ... das Style-Sheet!!!
	if( bCfgOutStyles )
	{
		OutStyleSheet( *pPageDesc );
	}

	// und nun ... das BASIC und JavaScript!
	if( pDoc->GetDocShell() )	// nur mit DocShell ist Basic moeglich
		OutBasic();

	DecIndentLevel();	// Inhalt von <HEAD> einruecken
	OutNewLine();
	HTMLOutFuncs::Out_AsciiTag( Strm(), OOO_STRING_SVTOOLS_HTML_head, sal_False );

	// der Body wird nicht eingerueckt, weil sonst alles eingerueckt waere!
	OutNewLine();
	sOut = '<';
	sOut += OOO_STRING_SVTOOLS_HTML_body;
	Strm() << sOut.GetBuffer();
	sOut.Erase();

	// language
	OutLanguage( eLang );

	// Textfarbe ausgeben, wenn sie an der Standard-Vorlage gesetzt ist
	// und sich geaendert hat.
	OutBodyColor( OOO_STRING_SVTOOLS_HTML_O_text,
                  pDoc->GetTxtCollFromPool( RES_POOLCOLL_STANDARD, false ),
				  *this );

	// Farben fuer (un)besuchte Links
	OutBodyColor( OOO_STRING_SVTOOLS_HTML_O_link,
				  pDoc->GetCharFmtFromPool( RES_POOLCHR_INET_NORMAL ),
				  *this );
	OutBodyColor( OOO_STRING_SVTOOLS_HTML_O_vlink,
				  pDoc->GetCharFmtFromPool( RES_POOLCHR_INET_VISIT ),
				  *this );

	const SfxItemSet& rItemSet = pPageDesc->GetMaster().GetAttrSet();

	String aEmbGrfName;
	OutBackground( rItemSet, aEmbGrfName, sal_True );

	nDirection = GetHTMLDirection( rItemSet );
	OutDirection( nDirection );

	if( bCfgOutStyles )
		OutCSS1_BodyTagStyleOpt( *this, rItemSet, aEmbGrfName );

	// Events anhaengen
	if( pDoc->GetDocShell() )	// nur mit DocShell ist Basic moeglich
		OutBasicBodyEvents();

	Strm() << '>';

	return pPageDesc;
}

void SwHTMLWriter::OutAnchor( const String& rName )
{
	ByteString sOut( '<' );
	(((sOut += OOO_STRING_SVTOOLS_HTML_anchor) += ' ') += OOO_STRING_SVTOOLS_HTML_O_name) += "=\"";
	Strm() << sOut.GetBuffer();
	HTMLOutFuncs::Out_String( Strm(), rName, eDestEnc, &aNonConvertableCharacters ) << "\">";
	HTMLOutFuncs::Out_AsciiTag( Strm(), OOO_STRING_SVTOOLS_HTML_anchor, sal_False );
}

void SwHTMLWriter::OutBookmarks()
{
    // hole das aktuelle Bookmark
    const ::sw::mark::IMark* pBookmark = NULL;
    IDocumentMarkAccess* const pMarkAccess = pDoc->getIDocumentMarkAccess();
    if(nBkmkTabPos != -1)
        pBookmark = (pMarkAccess->getAllMarksBegin() + nBkmkTabPos)->get();
    // Ausgabe aller Bookmarks in diesem Absatz. Die Content-Position
    // wird vorerst nicht beruecksichtigt!
    sal_uInt32 nNode = pCurPam->GetPoint()->nNode.GetIndex();
    while( nBkmkTabPos != -1
           && pBookmark->GetMarkPos().nNode.GetIndex() == nNode )
    {
        // Der Bereich derBookmark wird erstam ignoriert, da er von uns
        // auch nicht eingelesen wird.

        // erst die SWG spezifischen Daten:
        if ( dynamic_cast< const ::sw::mark::IBookmark* >(pBookmark) != NULL
             && pBookmark->GetName().getLength() )
        {
            OutAnchor( pBookmark->GetName() );
        }

        if( ++nBkmkTabPos >= pMarkAccess->getAllMarksCount() )
            nBkmkTabPos = -1;
        else
            pBookmark = (pMarkAccess->getAllMarksBegin() + nBkmkTabPos)->get();
    }

    sal_uInt16 nPos;
    for( nPos = 0; nPos < aOutlineMarkPoss.Count() &&
                   aOutlineMarkPoss[nPos] < nNode; nPos++ )
        ;

    while( nPos < aOutlineMarkPoss.Count() && aOutlineMarkPoss[nPos] == nNode )
    {
        String sMark( *aOutlineMarks[nPos] );
        sMark.SearchAndReplaceAll( '?', '_' );  // '?' causes problems in IE/Netscape 5
        OutAnchor( sMark );
        aOutlineMarkPoss.Remove( nPos, 1 );
        aOutlineMarks.DeleteAndDestroy( nPos, 1 );
    }
}

void SwHTMLWriter::OutImplicitMark( const String& rMark,
									const sal_Char *pMarkType )
{
	if( rMark.Len() && aImplicitMarks.Count() )
	{
		String sMark( rMark );
		sMark.Append( cMarkSeperator );
		sMark.AppendAscii( pMarkType );
		sal_uInt16 nPos;
		if( aImplicitMarks.Seek_Entry( &sMark, &nPos ) )
		{
			sMark.SearchAndReplaceAll( '?', '_' );	// '?' causes problems in IE/Netscape 5
			OutAnchor( sMark );
			aImplicitMarks.DeleteAndDestroy( nPos, 1 );
		}
	}
}

void SwHTMLWriter::OutHyperlinkHRefValue( const String& rURL )
{
	String sURL( rURL );
	xub_StrLen nPos = sURL.SearchBackward( cMarkSeperator );
	if( STRING_NOTFOUND != nPos )
	{
		String sCmp( sURL.Copy( nPos+1 ) );
		sCmp.EraseAllChars();
		if( sCmp.Len() )
		{
			sCmp.ToLowerAscii();
			if( sCmp.EqualsAscii( pMarkToRegion ) ||
				sCmp.EqualsAscii( pMarkToFrame ) ||
				sCmp.EqualsAscii( pMarkToGraphic ) ||
				sCmp.EqualsAscii( pMarkToOLE ) ||
				sCmp.EqualsAscii( pMarkToTable ) ||
				sCmp.EqualsAscii( pMarkToOutline ) ||
				sCmp.EqualsAscii( pMarkToText ) )
			{
				sURL.SearchAndReplaceAll( '?', '_' );	// '?' causes problems in IE/Netscape 5
			}
		}
	}

    sURL = URIHelper::simpleNormalizedMakeRelative( GetBaseURL(), sURL);
	HTMLOutFuncs::Out_String( Strm(), sURL, eDestEnc,
							  &aNonConvertableCharacters );
}

void SwHTMLWriter::OutBackground( const SvxBrushItem *pBrushItem,
								  String& rEmbGrfNm, sal_Bool bGraphic )
{
	const Color &rBackColor = pBrushItem->GetColor();
    /// OD 02.09.2002 #99657#
    /// check, if background color is not "no fill"/"auto fill", instead of
    /// only checking, if transparency is not set.
    if( rBackColor.GetColor() != COL_TRANSPARENT )
	{
		ByteString sOut( ' ' );
		(sOut += OOO_STRING_SVTOOLS_HTML_O_bgcolor) += '=';
		Strm() << sOut.GetBuffer();
		HTMLOutFuncs::Out_Color( Strm(), rBackColor, eDestEnc);
	}

	if( !bGraphic )
		return;

	const String *pLink = pBrushItem->GetGraphicLink();

	// embeddete Grafik -> WriteEmbedded schreiben
	if( !pLink )
	{
		const Graphic* pGrf = pBrushItem->GetGraphic();
		if( pGrf )
		{
			// Grafik als (JPG-)File speichern
			const String* pTempFileName = GetOrigFileName();
			if(pTempFileName)
				rEmbGrfNm = *pTempFileName;
			sal_uInt16 nErr = XOutBitmap::WriteGraphic( *pGrf, rEmbGrfNm,
					String::CreateFromAscii( "JPG" ),
					XOUTBMP_USE_NATIVE_IF_POSSIBLE );
			if( !nErr )		// fehlerhaft, da ist nichts auszugeben
			{
                rEmbGrfNm = URIHelper::SmartRel2Abs(
                    INetURLObject( GetBaseURL() ), rEmbGrfNm,
                    URIHelper::GetMaybeFileHdl() );
				pLink = &rEmbGrfNm;
			}
			else
			{
				nWarn = WARN_SWG_POOR_LOAD | WARN_SW_WRITE_BASE;
			}
		}
	}
	else
	{
		rEmbGrfNm = *pLink;
		if( bCfgCpyLinkedGrfs )
		{
			CopyLocalFileToINet( rEmbGrfNm  );
			pLink = &rEmbGrfNm;
		}
	}

	if( pLink )
	{
		ByteString sOut( ' ' );
        String s( URIHelper::simpleNormalizedMakeRelative( GetBaseURL(), *pLink));
		(sOut += OOO_STRING_SVTOOLS_HTML_O_background) += "=\"";
		Strm() << sOut.GetBuffer();
		HTMLOutFuncs::Out_String( Strm(), s, eDestEnc, &aNonConvertableCharacters ) << '\"';
	}
}

void SwHTMLWriter::OutBackground( const SfxItemSet& rItemSet,
								  String& rEmbGrfNm, sal_Bool bGraphic )
{
	const SfxPoolItem* pItem;
	if( SFX_ITEM_SET == rItemSet.GetItemState( RES_BACKGROUND, sal_False,
											   &pItem ))
	{
		OutBackground( ((const SvxBrushItem*)pItem), rEmbGrfNm, bGraphic );
	}
}

sal_uInt16 SwHTMLWriter::GetLangWhichIdFromScript( sal_uInt16 nScript )
{
	sal_uInt16 nWhichId;
	switch( nScript )
	{
	case CSS1_OUTMODE_CJK:
		nWhichId = RES_CHRATR_CJK_LANGUAGE;
		break;
	case CSS1_OUTMODE_CTL:
		nWhichId = RES_CHRATR_CJK_LANGUAGE;
		break;
	default:
		nWhichId = RES_CHRATR_LANGUAGE;
		break;
	}
	return nWhichId;
}

void SwHTMLWriter::OutLanguage( LanguageType nLang )
{
	if( LANGUAGE_DONTKNOW != nLang )
	{
		ByteString sOut( ' ' );
		(sOut += OOO_STRING_SVTOOLS_HTML_O_lang) += "=\"";
		Strm() << sOut.GetBuffer();
		HTMLOutFuncs::Out_String( Strm(), MsLangId::convertLanguageToIsoString(nLang),
								  eDestEnc, &aNonConvertableCharacters ) << '"';
	}
}

sal_uInt16 SwHTMLWriter::GetHTMLDirection( const SfxItemSet& rItemSet ) const
{
	return GetHTMLDirection(
		static_cast < const SvxFrameDirectionItem& >( rItemSet.Get( RES_FRAMEDIR ) )
			.GetValue() );
}

sal_uInt16 SwHTMLWriter::GetHTMLDirection( sal_uInt16 nDir ) const
{
	switch( nDir )
	{
	case FRMDIR_VERT_TOP_LEFT:
		nDir = FRMDIR_HORI_LEFT_TOP;
		break;
	case FRMDIR_VERT_TOP_RIGHT:
		nDir = FRMDIR_HORI_RIGHT_TOP;
		break;
	case FRMDIR_ENVIRONMENT:
		nDir = nDirection;
	}

	return nDir;
}

void SwHTMLWriter::OutDirection( sal_uInt16 nDir )
{
	const sal_Char *pValue = 0;
	switch( nDir )
	{
	case FRMDIR_HORI_LEFT_TOP:
	case FRMDIR_VERT_TOP_LEFT:
		pValue = "LTR";
		break;
	case FRMDIR_HORI_RIGHT_TOP:
	case FRMDIR_VERT_TOP_RIGHT:
		pValue = "RTL";
		break;
	}
	if( pValue != 0 )
	{
		ByteString sOut( ' ' );
		(((sOut += OOO_STRING_SVTOOLS_HTML_O_dir) += "=\"") += pValue) += '\"';
		Strm() << sOut.GetBuffer();
	}
}

void SwHTMLWriter::GetIndentString( ByteString& rStr, sal_uInt16 nIncLvl )
{
	// etwas umstaendlich, aber wir haben nur einen Indent-String!
	sal_uInt16 nLevel = nIndentLvl + nIncLvl;

	if( nLevel && nLevel <= MAX_INDENT_LEVEL)
	{
		sIndentTabs[nLevel] = 0;
		rStr = sIndentTabs;
		sIndentTabs[nLevel] = '\t';
	}
}

void SwHTMLWriter::OutNewLine( sal_Bool bCheck )
{
	if( !bCheck || (Strm().Tell()-nLastLFPos) > nIndentLvl )
	{
		Strm() << sNewLine;
		nLastLFPos = Strm().Tell();
	}

	if( nIndentLvl && nIndentLvl <= MAX_INDENT_LEVEL)
	{
		sIndentTabs[nIndentLvl] = 0;
		Strm() << sIndentTabs;
		sIndentTabs[nIndentLvl] = '\t';
	}
}

sal_uInt16 SwHTMLWriter::GetHTMLFontSize( sal_uInt32 nHeight ) const
{
	sal_uInt16 nSize = 1;
	for( sal_uInt16 i=6; i>0; i-- )
	{
		if( nHeight > (aFontHeights[i] + aFontHeights[i-1])/2 )
		{
			nSize = i+1;
			break;
		}
	}

	return nSize;
}

// Struktur speichert die aktuellen Daten des Writers zwischen, um
// einen anderen Dokument-Teil auszugeben, wie z.B. Header/Footer
HTMLSaveData::HTMLSaveData( SwHTMLWriter& rWriter, sal_uLong nStt,
							sal_uLong nEnd, sal_Bool bSaveNum,
	   					 	const SwFrmFmt *pFrmFmt	) :
	rWrt( rWriter ),
	pOldPam( rWrt.pCurPam ),
	pOldEnd( rWrt.GetEndPaM() ),
	pOldNumRuleInfo( 0 ),
	pOldNextNumRuleInfo( 0 ),
	nOldDefListLvl( rWrt.nDefListLvl ),
	nOldDirection( rWrt.nDirection ),
	bOldOutHeader( rWrt.bOutHeader ),
	bOldOutFooter( rWrt.bOutFooter ),
	bOldOutFlyFrame( rWrt.bOutFlyFrame )
{
	bOldWriteAll = rWrt.bWriteAll;

	rWrt.pCurPam = rWrt.NewSwPaM( *rWrt.pDoc, nStt, nEnd );

	// Tabelle in Sonderbereichen erkennen
	if( nStt != rWrt.pCurPam->GetMark()->nNode.GetIndex() )
	{
		const SwNode *pNd = rWrt.pDoc->GetNodes()[ nStt ];
		if( pNd->IsTableNode() || pNd->IsSectionNode() )
			rWrt.pCurPam->GetMark()->nNode = nStt;
	}

	rWrt.SetEndPaM( rWrt.pCurPam );
	rWrt.pCurPam->Exchange( );
	rWrt.bWriteAll = sal_True;
	rWrt.nDefListLvl = 0;
	rWrt.bOutHeader = rWrt.bOutFooter = sal_False;

	// Ggf. die aktuelle Numerierungs-Info merken, damit sie wieder
	// neu aufgenommen werden kann. Nur dann belibt auch die Numerierungs-
	// Info des nachsten Absatz gueltig.
	if( bSaveNum )
	{
		pOldNumRuleInfo = new SwHTMLNumRuleInfo( rWrt.GetNumInfo() );
		pOldNextNumRuleInfo = rWrt.GetNextNumInfo();
		rWrt.SetNextNumInfo( 0 );
	}
	else
	{
		rWrt.ClearNextNumInfo();
	}

	// Die Numerierung wird in jedem Fall unterbrochen.
	rWrt.GetNumInfo().Clear();

	if( pFrmFmt )
		rWrt.nDirection = rWrt.GetHTMLDirection( pFrmFmt->GetAttrSet() );
}


HTMLSaveData::~HTMLSaveData()
{
	delete rWrt.pCurPam;					// Pam wieder loeschen

	rWrt.pCurPam = pOldPam;
	rWrt.SetEndPaM( pOldEnd );
	rWrt.bWriteAll = bOldWriteAll;
    rWrt.nBkmkTabPos = bOldWriteAll ? rWrt.FindPos_Bkmk( *pOldPam->GetPoint() ) : -1;
	rWrt.nLastParaToken = 0;
	rWrt.nDefListLvl = nOldDefListLvl;
	rWrt.nDirection = nOldDirection;
	rWrt.bOutHeader = bOldOutHeader;
	rWrt.bOutFooter = bOldOutFooter;
	rWrt.bOutFlyFrame = bOldOutFlyFrame;

	// Ggf. die Numerierung von vor der Section fortsetzen. Die Numerierung
	// des naecshten Absatz wird in jedem Fall ungueltig.
	if( pOldNumRuleInfo )
	{
		rWrt.GetNumInfo().Set( *pOldNumRuleInfo );
		delete pOldNumRuleInfo;
		rWrt.SetNextNumInfo( pOldNextNumRuleInfo );
	}
	else
	{
		rWrt.GetNumInfo().Clear();
		rWrt.ClearNextNumInfo();
	}
}


void GetHTMLWriter( const String&, const String& rBaseURL, WriterRef& xRet )
{
    xRet = new SwHTMLWriter( rBaseURL );
}
