#include #include #include #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "wv.h" /* To apply a UPX.papx to a UPE.pap, set UPE.pap.istd equal to UPX.papx.istd, and then apply the UPX.papx.grpprl to UPE.pap. */ void wvAddPAPXFromBucket (PAP * apap, UPXF * upxf, STSH * stsh, wvStream * data) { U8 *pointer; U16 i = 0; U16 sprm; apap->istd = upxf->upx.papx.istd; if (upxf->cbUPX <= 2) return; wvTrace (("no is %d\n", upxf->cbUPX)); #ifdef SPRMTEST fprintf (stderr, "\n"); while (i < upxf->cbUPX - 2) { fprintf (stderr, "%x (%d) ", *(upxf->upx.papx.grpprl + i), *(upxf->upx.papx.grpprl + i)); i++; } fprintf (stderr, "\n"); i = 0; #endif /* while (i < upxf->cbUPX-2) */ while (i < upxf->cbUPX - 4) /* the end of the list is at -2, but there has to be a full sprm of len 2 as well */ { sprm = bread_16ubit (upxf->upx.papx.grpprl + i, &i); #ifdef SPRMTEST wvError (("sprm is %x\n", sprm)); #endif pointer = upxf->upx.papx.grpprl + i; if (i < upxf->cbUPX - 2) wvApplySprmFromBucket (WORD8, sprm, apap, NULL, NULL, stsh, pointer, &i, data); } } void wvAddPAPXFromBucket6 (PAP * apap, UPXF * upxf, STSH * stsh) { U8 *pointer; U16 i = 0; U16 sprm; U8 sprm8; apap->istd = upxf->upx.papx.istd; if (upxf->cbUPX <= 2) return; wvTrace (("no is %d\n", upxf->cbUPX)); #ifdef SPRMTEST fprintf (stderr, "\n"); while (i < upxf->cbUPX - 2) { fprintf (stderr, "%x (%d) ", *(upxf->upx.papx.grpprl + i), *(upxf->upx.papx.grpprl + i)); i++; } fprintf (stderr, "\n"); i = 0; #endif while (i < upxf->cbUPX - 3) /* the end of the list is at -2, but there has to be a full sprm of len 1 as well */ { sprm8 = bread_8ubit (upxf->upx.papx.grpprl + i, &i); #ifdef SPRMTEST wvError (("pap word 6 sprm is %x (%d)\n", sprm8, sprm8)); #endif sprm = (U16) wvGetrgsprmWord6 (sprm8); #ifdef SPRMTEST wvError (("pap word 6 sprm is converted to %x\n", sprm)); #endif pointer = upxf->upx.papx.grpprl + i; /* hmm, maybe im wrong here, but there appears to be corrupt * word 6 sprm lists being stored in the file */ if (i < upxf->cbUPX - 2) wvApplySprmFromBucket (WORD6, sprm, apap, NULL, NULL, stsh, pointer, &i, NULL); } } void wvInitPAPFromIstd (PAP * apap, U16 istdBase, STSH * stsh) { if (istdBase == istdNil) wvInitPAP (apap); else { if (istdBase >= stsh->Stshi.cstd) { wvError ( ("ISTD out of bounds, requested %d of %d\n", istdBase, stsh->Stshi.cstd)); wvInitPAP (apap); /*it can't hurt to try and start with a blank istd */ return; } else { if (stsh->std[istdBase].cupx == 0) /*empty slot in the array, i don't think this should happen */ { wvTrace (("Empty style slot used (chp)\n")); wvInitPAP (apap); } else { wvCopyPAP (apap, &(stsh->std[istdBase].grupe[0].apap)); strncpy(apap->stylename,stsh->std[istdBase].xstzName, sizeof(apap->stylename)); } } } } void wvCopyPAP (PAP * dest, PAP * src) { memcpy (dest, src, sizeof (PAP)); } void wvInitPAP (PAP * item) { int i; item->istd = 0; item->jc = 0; item->fKeep = 0; item->fKeepFollow = 0; item->fPageBreakBefore = 0; item->fBrLnAbove = 0; item->fBrLnBelow = 0; item->fUnused = 0; item->pcVert = 0; item->pcHorz = 0; item->brcp = 0; item->brcl = 0; item->reserved1 = 0; item->ilvl = 0; item->fNoLnn = 0; item->ilfo = 0; item->nLvlAnm = 0; item->reserved2 = 0; item->fSideBySide = 0; item->reserved3 = 0; item->fNoAutoHyph = 0; item->fWidowControl = 1; item->dxaRight = 0; item->dxaLeft = 0; item->dxaLeft1 = 0; /* wvInitLSPD(&item->lspd); */ item->lspd.fMultLinespace = 1; item->lspd.dyaLine = 240; item->dyaBefore = 0; item->dyaAfter = 0; wvInitPHE (&item->phe, 0); item->fCrLf = 0; item->fUsePgsuSettings = 0; item->fAdjustRight = 0; item->reserved4 = 0; item->fKinsoku = 0; item->fWordWrap = 0; item->fOverflowPunct = 0; item->fTopLinePunct = 0; item->fAutoSpaceDE = 0; item->fAtuoSpaceDN = 0; item->wAlignFont = 0; item->fVertical = 0; item->fBackward = 0; item->fRotateFont = 0; item->reserved5 = 0; item->reserved6 = 0; item->fInTable = 0; item->fTtp = 0; item->wr = 0; item->fLocked = 0; wvInitTAP (&item->ptap); item->dxaAbs = 0; item->dyaAbs = 0; item->dxaWidth = 0; wvInitBRC (&item->brcTop); wvInitBRC (&item->brcLeft); wvInitBRC (&item->brcBottom); wvInitBRC (&item->brcRight); wvInitBRC (&item->brcBetween); wvInitBRC (&item->brcBar); item->dxaFromText = 0; item->dyaFromText = 0; item->dyaHeight = 0; item->fMinHeight = 0; wvInitSHD (&item->shd); wvInitDCS (&item->dcs); item->lvl = 9; item->fNumRMIns = 0; wvInitANLD (&item->anld); item->fPropRMark = 0; item->ibstPropRMark = 0; wvInitDTTM (&item->dttmPropRMark); wvInitNUMRM (&item->numrm); item->itbdMac = 0; for (i = 0; i < itbdMax; i++) item->rgdxaTab[i] = 0; for (i = 0; i < itbdMax; i++) wvInitTBD (&item->rgtbd[i]); item->fBidi = 0; item->stylename[0] = 0; memset(&item->linfo,0,sizeof(item->linfo)); } /* 1) Having found the index i of the FC in an FKP that marks the character stored in the file immediately after the paragraph's paragraph mark, 1 is done in Simple mode through wvGetSimpleParaBounds which places this index in fcLim by default 2) it is necessary to use the word offset stored in the first byte of the fkp.rgbx[i - 1] to find the PAPX for the paragraph. 3) Using papx.istd to index into the properties stored for the style sheet , 4) the paragraph properties of the style are copied to a local PAP. 5) Then the grpprl stored in the PAPX is applied to the local PAP, 6) and papx.istd along with fkp.rgbx.phe are moved into the local PAP. 7) The process thus far has created a PAP that describes what the paragraph properties of the paragraph were at the last full save. */ int wvAssembleSimplePAP (wvVersion ver, PAP * apap, U32 fc, PAPX_FKP * fkp, wvParseStruct * ps) { PAPX *papx; int index; UPXF upxf; int ret = 0; /* list processing vars */ U32 myListId = 0; LVLF * myLVLF = NULL; LVL * myLVL = NULL; LFO * myLFO = NULL; LST * myLST = NULL; LFOLVL * myLFOLVL = NULL; S32 myStartAt = -1; U8 * mygPAPX = NULL; U8 * mygCHPX = NULL; XCHAR * myNumberStr = NULL; S32 myNumberStr_count = 0; U32 mygPAPX_count = 0, mygCHPX_count = 0; PAPX myPAPX; CHPX myCHPX; S32 i = 0, j = 0, k = 0; int bNeedLST_LVL; int bLST_LVL_format; LVL * prevLVL; LVLF * prevLVLF; /*index is the i in the text above */ index = wvGetIndexFCInFKP_PAPX (fkp, fc); wvTrace (("index is %d, using %d\n", index, index - 1)); papx = &(fkp->grppapx[index - 1]); if (papx) { wvTrace (("istd index is %d\n", papx->istd)); wvInitPAPFromIstd (apap, papx->istd, &ps->stsh); } else wvInitPAPFromIstd (apap, istdNil, &ps->stsh); if ((papx) && (papx->cb > 2)) { ret = 1; #ifdef SPRMTEST fprintf (stderr, "cbUPX is %d\n", papx->cb); for (i = 0; i < papx->cb - 2; i++) fprintf (stderr, "%x ", papx->grpprl[i]); fprintf (stderr, "\n"); #endif upxf.cbUPX = papx->cb; upxf.upx.papx.istd = papx->istd; upxf.upx.papx.grpprl = papx->grpprl; if (ver == WORD8) wvAddPAPXFromBucket (apap, &upxf, &ps->stsh, ps->data); else wvAddPAPXFromBucket6 (apap, &upxf, &ps->stsh); } if (papx) apap->istd = papx->istd; if (fkp->rgbx != NULL) wvCopyPHE (&apap->phe, &(fkp->rgbx[index - 1].phe), apap->fTtp); /* By now we have assembled the paragraph properties based on the info in the style associated with this pap and also in any of the PAPX overrides; next step is to see if this paragraph is a part of a list, and if so, to apply any list-specific overrides The MS documentation on lists really sucks, but we've been able to decipher some meaning from it and get simple lists to sorta work. This code mostly prints out debug messages with useful information in them, but it will also append a list and add a given paragraph to a given list */ if (!apap->ilfo || apap->ilfo > ps->nolfo) return ret; /* This is really silly, but it would seem that if there are both PAPX for the paragraph and the list, the paragraph ones take priority (basically, when a list is applied to a custom indented block, the block's indents become part of the list PAPX; if the indents of the block are subsequently modified, the PAPX of the list stays the same, and the PAPX of the block changes); this means that we now have to apply the list PAPX over what we have and then reapply the block PAPX (we had to apply the block's PAPX in order to find out if we are in a list !!!)*/ if (!ps->lfo) return ret; wvTrace(("list: ilvl %d, ilfo %d\n",apap->ilvl,apap->ilfo)); /* ilvl is the list level */ /* first, get the LFO, and then find the lfovl for this paragraph */ if (ps->lfo) myLFO = &ps->lfo[apap->ilfo - 1]; while(i < (S32)apap->ilfo - 1 && i < (S32)ps->nolfo) { j += ps->lfo[i].clfolvl; i++; } /* remember how many overrides are there for this record */ if (ps->lfo) k = ps->lfo[i].clfolvl; else k = 0; /* if there are any overrides, then see if one of them applies to this level */ if(k && ps->lfolvl) { i = 0; while(i < k && ps->lfolvl[j].ilvl != apap->ilvl) { j++; i++; } if(i >= k) { wvTrace(("list: no LFOLVL found for this level (1)\n")); myLFOLVL = NULL; } else { myLFOLVL = &ps->lfolvl[j]; wvTrace(("list: lfovl: iStartAt %d, fStartAt\n", myLFOLVL->iStartAt,myLFOLVL->fStartAt,myLFOLVL->fFormatting)); if(!myLFOLVL->fFormatting && myLFOLVL->fStartAt) myStartAt = myLFOLVL->iStartAt; } } else { wvTrace(("list: no LFOLVL found for this level (2)\n")); myLFOLVL = NULL; } /* now that we might have the LFOLVL, let's see if we should use the LVL from the LFO */ bNeedLST_LVL = (!myLFOLVL || !myLFOLVL->fStartAt || !myLFOLVL->fFormatting); bLST_LVL_format = 1; if(myLFOLVL) { /* this branch has not been (thoroughly) debugged Abi bugs 2205 and 2393 exhibit this behavior */ wvTrace(("list: using the LVL from LFO\n")); myListId = myLFOLVL->iStartAt; i = 0; wvTrace(("list: number of LSTs %d, my lsid %d\n", ps->noofLST,myListId)); while(i < ps->noofLST && ps->lst[i].lstf.lsid != myListId) { i++; wvTrace(("list: lsid in LST %d\n", ps->lst[i-1].lstf.lsid)); } if(i == ps->noofLST || ps->lst[i].lstf.lsid != myListId) { wvTrace(("error: could not locate LST entry\n")); goto list_error; } myLST = &ps->lst[i]; myLVL = &myLST->lvl[apap->ilvl]; /* now we should have the LVL */ if(!myLVL) return ret; myLVLF = &myLVL->lvlf; if(!myLVLF) return ret; myStartAt = myLFOLVL->fStartAt ? (S32)(myLVLF->iStartAt) : -1; mygPAPX = myLFOLVL->fFormatting ? myLVL->grpprlPapx : NULL; mygPAPX_count = myLFOLVL->fFormatting ? myLVLF->cbGrpprlPapx : 0; /* not sure about this, the CHPX applies to the number, so it might be that we should take this if the fStartAt is set -- the docs are not clear */ mygCHPX = myLFOLVL->fFormatting ? myLVL->grpprlChpx : NULL; mygCHPX_count = myLFOLVL->fFormatting ? myLVLF->cbGrpprlChpx : 0; myNumberStr = myLFOLVL->fStartAt && myLVL->numbertext ? myLVL->numbertext + 1 : NULL; myNumberStr_count = myNumberStr ? *(myLVL->numbertext) : 0; if(myLFOLVL->fFormatting) bLST_LVL_format = 0; } if(bNeedLST_LVL) { prevLVL = myLVL; prevLVLF = myLVLF; myListId = myLFO ? myLFO->lsid : 0; wvTrace(("list: using the LVL from LST\n")); i = 0; wvTrace(("list: number of LSTs %d, my lsid %d\n", ps->noofLST,myListId)); while(i < ps->noofLST && ps->lst[i].lstf.lsid != myListId) { i++; wvTrace(("list: lsid in LST %d\n", ps->lst[i-1].lstf.lsid)); } if(i == ps->noofLST || ps->lst[i].lstf.lsid != myListId) { wvTrace(("error: could not locate LST entry\n")); goto list_error; } myLST = &ps->lst[i]; myLVL = &myLST->lvl[apap->ilvl]; /* now we should have the correct LVL */ if(!myLVL) return ret; myLVLF = &myLVL->lvlf; if(!myLVLF) return ret; /* retrieve any stuff we need from here (i.e., only what we did not get from the LFO LVL) */ myStartAt = myStartAt == -1 ? myLVLF->iStartAt : myStartAt; mygPAPX_count = !mygPAPX ? myLVLF->cbGrpprlPapx : mygPAPX_count; mygPAPX = !mygPAPX ? myLVL->grpprlPapx : mygPAPX; mygCHPX_count = !mygCHPX ? myLVLF->cbGrpprlChpx : mygCHPX_count; mygCHPX = !mygCHPX ? myLVL->grpprlChpx : mygCHPX; myNumberStr_count = !myNumberStr && myLVL->numbertext ? *(myLVL->numbertext) : myNumberStr_count; myNumberStr = !myNumberStr && myLVL->numbertext ? myLVL->numbertext + 1 : myNumberStr; /* if there was a valid LFO LVL record that pertained to formatting then we will set the myLVL and myLVLF variables back to this record so that it can be used */ if(!bLST_LVL_format && prevLVL && prevLVLF) { myLVL = prevLVL; myLVLF = prevLVLF; } } wvTrace(("list: number text len %d, papx len %d, chpx len%d\n",myNumberStr_count,mygPAPX_count,mygCHPX_count)); myPAPX.cb = mygPAPX_count; myPAPX.grpprl = mygPAPX; myPAPX.istd = apap->istd; /* IMPORTANT now we have the list formatting sutff retrieved; it is found in several different places: apap->ilvl - the level of this list (0-8) myStartAt - the value at which the numbering for this listshould start (i.e., the number of the first item on the list) myListId - the id of this list, we need this to know to which list this paragraph belongs; unfortunately, there seem to be some cases where separate lists *share* the same id, for instance when two lists, of different formatting, are separated by only empty paragraphs. As a hack, AW will add the format number to the list id, so gaining different id for different formattings (it is not foolproof, for if id1 + format1 == id2 + format2 then we get two lists joined, but the probability of that should be small). Further problem is that in AW, list id refers to the set of list elements on the same level, while in Word the id is that of the entire list. The easiest way to tranform the Word id to AW id is to add the level to the id PAPX - the formatting information that needs to be added to the format of this list CHPX - the formatting of the list number myNumberStr - the actual number string to display (XCHAR *); we probably need this to work out the number separator, since there does not seem to be any reference to this anywhere myNumberStr_count - length of the number string myLVLF->nfc - number format (see the enum below) myLVLF->jc - number alignment [0: lft, 1: rght, 2: cntr] myLVLF->ixchFollow - what character stands between the number and the para [0:= tab, 1: spc, 2: none] we shall copy this info, except the ilvl, to the wv extension of the PAP structure */ wvTrace(("list: id %d \n",myListId)); wvTrace(("list: iStartAt %d\n", myStartAt)); wvTrace(("list: lvlf: format %d\n",myLVLF->nfc)); /* see the comment above for nfc values */ wvTrace(("list: lvlf: number align %d [0: lft, 1: rght, 2: cntr]\n",myLVLF->jc)); wvTrace(("list: lvlf: ixchFollow %d [0:= tab, 1: spc, 2: none]\n",myLVLF->ixchFollow)); apap->linfo.id = myListId; apap->linfo.start = myStartAt; apap->linfo.numberstr = myNumberStr; apap->linfo.numberstr_size = myNumberStr_count; apap->linfo.format = myLVLF->nfc; apap->linfo.align = myLVLF->jc; apap->linfo.ixchFollow = myLVLF->ixchFollow; /* the number formatting */ myCHPX.cbGrpprl = mygCHPX_count; myCHPX.grpprl = mygCHPX; myCHPX.istd = 4095; /* next we need to apply the list PAPX to our PAP */ if (myPAPX.cb > 2) { ret = 1; upxf.cbUPX = myPAPX.cb; upxf.upx.papx.istd = myPAPX.istd; upxf.upx.papx.grpprl = myPAPX.grpprl; if (ver == WORD8) wvAddPAPXFromBucket (apap, &upxf, &ps->stsh, ps->data); else wvAddPAPXFromBucket6 (apap, &upxf, &ps->stsh); /* now we have to reapply the original PAPX, see note at top of the list code */ if((papx) && (papx->cb > 2)) { ret = 1; upxf.cbUPX = papx->cb; upxf.upx.papx.istd = papx->istd; upxf.upx.papx.grpprl = papx->grpprl; if (ver == WORD8) wvAddPAPXFromBucket (apap, &upxf, &ps->stsh, ps->data); else wvAddPAPXFromBucket6 (apap, &upxf, &ps->stsh); } } /* next see if the list number comes with additional char formatting information; if it does, we will stre it the linfo.chp */ if(myCHPX.cbGrpprl) { ret = 1; wvAssembleSimpleCHP(ver, &apap->linfo.chp, apap, 0, NULL, &ps->stsh); upxf.cbUPX = myCHPX.cbGrpprl; upxf.upx.chpx.grpprl = myCHPX.grpprl; if (ver == WORD8) wvAddCHPXFromBucket (&apap->linfo.chp, &upxf, &ps->stsh); else wvAddCHPXFromBucket6 (&apap->linfo.chp, &upxf, &ps->stsh); } if (myPAPX.istd != istdNil) apap->istd = myPAPX.istd; list_error: return (ret); } void wvReleasePAPX (PAPX * item) { item->cb = 0; item->istd = 0; wvFree (item->grpprl); item->grpprl = NULL; } void wvInitPAPX (PAPX * item) { item->cb = 0; item->istd = 0; item->grpprl = NULL; } void wvGetPAPX (wvVersion ver, PAPX * item, U8 * page, U16 * pos) { U16 cw; cw = bread_8ubit (&(page[*pos]), pos); if ((cw == 0) && (ver == WORD8)) /* only do this for word 97 */ { wvTrace (("cw was pad %d\n", cw)); cw = bread_8ubit (&(page[*pos]), pos); wvTrace (("cw was %d\n", cw)); } item->cb = cw * 2; item->istd = bread_16ubit (&(page[*pos]), pos); wvTrace (("papx istd is %x\n", item->istd)); wvTrace (("no of bytes is %d\n", item->cb)); if (item->cb > 2) { item->grpprl = (U8 *) malloc (item->cb - 2); memcpy (item->grpprl, &(page[*pos]), (item->cb) - 2); } else item->grpprl = NULL; } int isPAPConform (PAP * current, PAP * previous) { if ((current) && (previous)) if (wvEqualBRC (¤t->brcLeft, &previous->brcLeft)) if (wvEqualBRC (¤t->brcRight, &previous->brcRight)) if (current->dxaWidth == previous->dxaWidth) if (current->fInTable == previous->fInTable) return (1); return (0); } void wvCopyConformPAP (PAP * dest, PAP * src) { if (src) { #ifdef PURIFY wvInitPAP (dest); #endif dest->brcLeft = src->brcLeft; dest->brcRight = src->brcRight; dest->dxaWidth = src->dxaWidth; dest->fInTable = src->fInTable; } else wvInitPAP (dest); }