
Imagina una plantilla de contrato que se envía a decenas de clientes. El equipo legal ha redactado con cuidado cada cláusula, y lo único que cada destinatario debería tocar son el bloque de firma, el nombre del proyecto y la fecha de aceptación. Entrega un archivo de Word totalmente editable y alguien acabará reformulando una cláusula de penalización o eliminando una sección de responsabilidad. Bloquea todo el documento y nadie podrá rellenar los campos. Lo que realmente necesitas es edición selectiva: una forma de decir "estos párrafos concretos son editables, todo lo demás está congelado".
Eso es exactamente lo que te ofrecen los rangos editables. Proteges todo el documento como solo lectura y luego colocas un par de marcadores de permiso alrededor de los párrafos que quieres mantener abiertos. Cualquiera que abra el archivo en Word puede escribir dentro de la región marcada, pero no puede alterar ni un solo carácter fuera de ella. Spire.Doc for JavaScript lleva esta capacidad al navegador mediante WebAssembly, de modo que puedes generar documentos protegidos desde una aplicación React sin idas y vueltas al servidor: las fuentes y los archivos de entrada se gestionan a través de un sistema de archivos virtual (VFS) en memoria.
Esta guía recorre ambas partes del flujo de trabajo:
- Establecer un rango editable: protege el documento y marca el área abierta
- Eliminar un rango editable: quita los marcadores y levanta la restricción
Si aún no has integrado Spire.Doc en tu proyecto, empieza por Integrar Spire.Doc for JavaScript en un proyecto de React. Los fragmentos siguientes suponen que el módulo WebAssembly está cargado y listo.
Establecer un rango editable
El proceso tiene tres etapas. Primero, carga los archivos de fuentes y el documento de Word de destino en el sistema de archivos virtual de WASM con FetchFileToVFS. A continuación, crea una instancia de Document, carga el archivo, llama a Protect para bloquear todo el documento como solo lectura y luego crea un par PermissionStart / PermissionEnd que comparta el mismo id: estos dos marcadores delimitan el párrafo que quieres dejar editable. Por último, guarda el archivo, vuelve a leerlo desde el VFS, envuélvelo en un Blob y activa la descarga.
function App() {
const SetEditableRange = async () => {
const docModule = window.wasmModule?.spiredoc;
if (!docModule) {
alert('Spire.Doc is not ready yet');
return;
}
// Load the input document into VFS
const inputFileName = "SetEditableRange.docx";
await window.spire.FetchFileToVFS(inputFileName, "", `${process.env.PUBLIC_URL}static/data/`);
// Create a document object and load the document
const doc = new docModule.Document();
doc.LoadFromFile(inputFileName);
// Protect the whole document: everything outside the editable range is read-only
doc.Protect({ type: docModule.ProtectionType.AllowOnlyReading, password: "password" });
// Create the permission markers: a start and an end with the same id form one editable range
const start = new docModule.PermissionStart(doc, "testID");
const end = new docModule.PermissionEnd(doc, "testID");
// Insert the markers into the first paragraph: the start at the beginning, the end appended at the end
doc.Sections.get_Item(0).Paragraphs.get_Item(0).ChildObjects.Insert(0, start);
doc.Sections.get_Item(0).Paragraphs.get_Item(0).ChildObjects.Add(end);
// Save the document
const outputFileName = "Set Editable Range.docx";
doc.SaveToFile({ fileName: outputFileName, fileFormat: docModule.FileFormat.Docx2013 });
doc.Dispose();
const modifiedFileArray = window.dotnetRuntime.Module.FS.readFile(outputFileName);
const blob = new Blob([modifiedFileArray], { type: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = outputFileName;
a.click();
URL.revokeObjectURL(url);
};
return (
<div style={{ textAlign: 'center', height: '300px' }}>
<h1>Set Editable Range in a Word Document</h1>
<button onClick={SetEditableRange}>
Generate
</button>
</div>
);
}
export default App;
En el archivo de ejemplo, los campos que el revisor puede rellenar llevan un sombreado claro: es solo una pista visual para el lector y no influye en cómo se define el rango editable en el código. Una vez colocados los marcadores, Word trata el párrafo sombreado como editable y todos los demás como bloqueados.

Eliminar un rango editable
Quitar el rango editable es un único recorrido: itera por cada sección y cada párrafo, inspecciona cada objeto de la colección ChildObjects del párrafo y extrae cualquier cosa que sea un PermissionStart o un PermissionEnd.
Un detalle sutil suele pillar desprevenidos a muchos: ChildObjects.Remove reduce la colección en el acto, por lo que cada elemento posterior al eliminado se desplaza un índice hacia delante. Si incrementas el contador del bucle mientras eliminas, cada eliminación provoca que se omita el marcador inmediatamente siguiente, y cuantos más marcadores tengas, más supervivientes dejarás atrás.
function App() {
const RemoveEditableRange = async () => {
const docModule = window.wasmModule?.spiredoc;
if (!docModule) {
alert('Spire.Doc is not ready yet');
return;
}
// Load the input document into VFS
const inputFileName = "RemoveEditableRange.docx";
await window.spire.FetchFileToVFS(inputFileName, "", `${process.env.PUBLIC_URL}static/data/`);
// Create a document object and load the document
const doc = new docModule.Document();
doc.LoadFromFile(inputFileName);
// Iterate over every section and paragraph and delete the permission markers
for (let i = 0; i < doc.Sections.Count; i++) {
const section = doc.Sections.get_Item(i);
for (let j = 0; j < section.Body.Paragraphs.Count; j++) {
const paragraph = section.Body.Paragraphs.get_Item(j);
// Remove on a match; the collection shrinks, so the index is not incremented
for (let k = 0; k < paragraph.ChildObjects.Count;) {
const obj = paragraph.ChildObjects.get_Item(k);
if (obj instanceof docModule.PermissionStart || obj instanceof docModule.PermissionEnd) {
paragraph.ChildObjects.Remove(obj);
} else {
k++;
}
}
}
}
// Save the document
const outputFileName = "Remove Editable Range.docx";
doc.SaveToFile({ fileName: outputFileName, fileFormat: docModule.FileFormat.Docx2013 });
// Release resources
doc.Dispose();
const modifiedFileArray = window.dotnetRuntime.Module.FS.readFile(outputFileName);
const blob = new Blob([modifiedFileArray], { type: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = outputFileName;
a.click();
URL.revokeObjectURL(url);
};
return (
<div style={{ textAlign: 'center', height: '300px' }}>
<h1>Remove Editable Ranges from a Word Document</h1>
<button onClick={RemoveEditableRange}>
Generate
</button>
</div>
);
}
export default App;
Eliminar los marcadores solo redibuja el límite de lo que es editable: el texto en sí y todo el formato permanecen intactos.

El ciclo de vida completo de la protección
En un flujo de trabajo de aprobación real, rara vez haces solo una cosa. Un ciclo típico de ida y vuelta es así:
-
Proteger: llama a
doc.ProtectconAllowOnlyReading(oAllowOnlyFormFields) y una contraseña. Todo el documento queda bloqueado. -
Marcar: envuelve cada párrafo editable por el revisor en un par
PermissionStart/PermissionEndque comparta un mismo id. Esas regiones pasan a ser los únicos lugares donde el revisor puede escribir. - Desmarcar: cuando termina la ronda de revisión, recorre el documento y elimina todos los marcadores de permiso. Las regiones vuelven a formar parte del cuerpo de solo lectura.
-
Desproteger: llama a
doc.Unprotect("password")para liberar el documento por completo, devolviéndolo a un estado totalmente editable para la siguiente etapa del procesamiento.
La clave está en que la protección y los rangos editables son dos capas independientes. La protección decide si el documento está bloqueado; el par de marcadores decide qué pequeñas porciones quedan exentas de ese bloqueo. Puedes añadir y eliminar marcadores tantas veces como quieras sin tocar el estado de protección, y puedes activar o desactivar la protección sin alterar los marcadores, pero los marcadores solo tienen efecto mientras la protección está activa.
Preguntas frecuentes
El rango editable está establecido, pero el contenido dentro de él aún no se puede editar
Por qué ocurre: los marcadores de permiso son inertes por sí solos. Solo establecen excepciones a una restricción que abarca todo el documento, así que si nunca se llamó a Protect, no hay ninguna restricción de la que eximirse y los marcadores no hacen nada. Un segundo requisito es que PermissionStart y PermissionEnd lleven la misma cadena de id: Word solo los trata como un par cuando los ids coinciden.
Solución: activa primero la restricción de edición y luego crea ambos marcadores con un id idéntico:
// Enable protection first so that the markers mean something
document.Protect({ type: wasmModule.ProtectionType.AllowOnlyReading, password: "password" });
// The start and the end must use the same id
const start = new wasmModule.PermissionStart(document, "testID");
const end = new wasmModule.PermissionEnd(document, "testID");
Algunos marcadores se omiten al eliminar rangos editables
Por qué ocurre: cada llamada a ChildObjects.Remove reduce la colección en uno, desplazando hacia abajo el índice de todos los elementos siguientes. Si el contador del bucle avanza en la misma iteración que una eliminación, el elemento que se deslizó a la posición actual nunca se examina: se omite, y el problema se agrava con cada marcador adicional.
Solución: mantén el índice fijo mientras eliminas (avánzalo solo cuando no se haya producido ninguna eliminación) o reúne primero los objetos objetivo y elimínalos en orden inverso:
for (let k = 0; k < paragraph.ChildObjects.Count;) {
const obj = paragraph.ChildObjects.get_Item(k);
if (obj instanceof wasmModule.PermissionStart || obj instanceof wasmModule.PermissionEnd) {
paragraph.ChildObjects.Remove(obj);
// Do not increment k here: check the new object at the current index
} else {
k++;
}
}
El documento sigue siendo de solo lectura después de eliminar los marcadores
Por qué ocurre: los marcadores solo definen qué áreas quedan exentas del bloqueo; no son el bloqueo en sí. Eliminarlos simplemente quita las exenciones; la protección subyacente que estableció Protect sigue vigente, por lo que todo el documento permanece de solo lectura.
Solución: una vez que los marcadores han desaparecido y ya no necesitas la restricción, llama a Unprotect con la contraseña original:
document.Unprotect("password");